1- name: 2d.path.initial
2  testing:
3  - 2d.path.initial
4  #mozilla: { bug: TODO }
5  code: |
6    ctx.fillStyle = '#0f0';
7    ctx.fillRect(0, 0, 100, 50);
8    ctx.closePath();
9    ctx.fillStyle = '#f00';
10    ctx.fill();
11    @assert pixel 50,25 == 0,255,0,255;
12  expected: green
13
14- name: 2d.path.beginPath
15  testing:
16  - 2d.path.beginPath
17  code: |
18    ctx.fillStyle = '#0f0';
19    ctx.fillRect(0, 0, 100, 50);
20    ctx.rect(0, 0, 100, 50);
21    ctx.beginPath();
22    ctx.fillStyle = '#f00';
23    ctx.fill();
24    @assert pixel 50,25 == 0,255,0,255;
25  expected: green
26
27- name: 2d.path.moveTo.basic
28  testing:
29  - 2d.path.moveTo
30  code: |
31    ctx.fillStyle = '#f00';
32    ctx.fillRect(0, 0, 100, 50);
33    ctx.rect(0, 0, 10, 50);
34    ctx.moveTo(100, 0);
35    ctx.lineTo(10, 0);
36    ctx.lineTo(10, 50);
37    ctx.lineTo(100, 50);
38    ctx.fillStyle = '#0f0';
39    ctx.fill();
40    @assert pixel 90,25 == 0,255,0,255;
41  expected: green
42
43- name: 2d.path.moveTo.newsubpath
44  testing:
45  - 2d.path.moveTo
46  code: |
47    ctx.fillStyle = '#0f0';
48    ctx.fillRect(0, 0, 100, 50);
49    ctx.beginPath();
50    ctx.moveTo(0, 0);
51    ctx.moveTo(100, 0);
52    ctx.moveTo(100, 50);
53    ctx.moveTo(0, 50);
54    ctx.fillStyle = '#f00';
55    ctx.fill();
56    @assert pixel 50,25 == 0,255,0,255;
57  expected: green
58
59- name: 2d.path.moveTo.multiple
60  testing:
61  - 2d.path.moveTo
62  code: |
63    ctx.fillStyle = '#f00';
64    ctx.fillRect(0, 0, 100, 50);
65    ctx.moveTo(0, 25);
66    ctx.moveTo(100, 25);
67    ctx.moveTo(0, 25);
68    ctx.lineTo(100, 25);
69    ctx.strokeStyle = '#0f0';
70    ctx.lineWidth = 50;
71    ctx.stroke();
72    @assert pixel 50,25 == 0,255,0,255;
73  expected: green
74
75- name: 2d.path.moveTo.nonfinite
76  desc: moveTo() with Infinity/NaN is ignored
77  testing:
78  - 2d.nonfinite
79  code: |
80    ctx.moveTo(0, 0);
81    ctx.lineTo(100, 0);
82    @nonfinite ctx.moveTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
83    ctx.lineTo(100, 50);
84    ctx.lineTo(0, 50);
85    ctx.fillStyle = '#0f0';
86    ctx.fill();
87    @assert pixel 50,25 == 0,255,0,255;
88  expected: green
89
90- name: 2d.path.closePath.empty
91  testing:
92  - 2d.path.closePath.empty
93  code: |
94    ctx.fillStyle = '#0f0';
95    ctx.fillRect(0, 0, 100, 50);
96    ctx.closePath();
97    ctx.fillStyle = '#f00';
98    ctx.fill();
99    @assert pixel 50,25 == 0,255,0,255;
100  expected: green
101
102- name: 2d.path.closePath.newline
103  testing:
104  - 2d.path.closePath.nonempty
105  code: |
106    ctx.fillStyle = '#f00';
107    ctx.fillRect(0, 0, 100, 50);
108    ctx.strokeStyle = '#0f0';
109    ctx.lineWidth = 50;
110    ctx.moveTo(-100, 25);
111    ctx.lineTo(-100, -100);
112    ctx.lineTo(200, -100);
113    ctx.lineTo(200, 25);
114    ctx.closePath();
115    ctx.stroke();
116    @assert pixel 50,25 == 0,255,0,255;
117  expected: green
118
119- name: 2d.path.closePath.nextpoint
120  testing:
121  - 2d.path.closePath.nonempty
122  code: |
123    ctx.fillStyle = '#f00';
124    ctx.fillRect(0, 0, 100, 50);
125    ctx.strokeStyle = '#0f0';
126    ctx.lineWidth = 50;
127    ctx.moveTo(-100, 25);
128    ctx.lineTo(-100, -1000);
129    ctx.closePath();
130    ctx.lineTo(1000, 25);
131    ctx.stroke();
132    @assert pixel 50,25 == 0,255,0,255;
133  expected: green
134
135- name: 2d.path.lineTo.ensuresubpath.1
136  desc: If there is no subpath, the point is added and nothing is drawn
137  testing:
138  - 2d.path.lineTo.empty
139  - 2d.path.ensure
140  code: |
141    ctx.fillStyle = '#0f0';
142    ctx.fillRect(0, 0, 100, 50);
143    ctx.strokeStyle = '#f00';
144    ctx.lineWidth = 50;
145    ctx.beginPath();
146    ctx.lineTo(100, 50);
147    ctx.stroke();
148    @assert pixel 50,25 == 0,255,0,255;
149  expected: green
150
151- name: 2d.path.lineTo.ensuresubpath.2
152  desc: If there is no subpath, the point is added and used for subsequent drawing
153  testing:
154  - 2d.path.lineTo.empty
155  - 2d.path.ensure
156  code: |
157    ctx.fillStyle = '#f00';
158    ctx.fillRect(0, 0, 100, 50);
159    ctx.strokeStyle = '#0f0';
160    ctx.lineWidth = 50;
161    ctx.beginPath();
162    ctx.lineTo(0, 25);
163    ctx.lineTo(100, 25);
164    ctx.stroke();
165    @assert pixel 50,25 == 0,255,0,255;
166  expected: green
167
168- name: 2d.path.lineTo.basic
169  testing:
170  - 2d.path.lineTo.nonempty
171  code: |
172    ctx.fillStyle = '#f00';
173    ctx.fillRect(0, 0, 100, 50);
174    ctx.strokeStyle = '#0f0';
175    ctx.lineWidth = 50;
176    ctx.beginPath();
177    ctx.moveTo(0, 25);
178    ctx.lineTo(100, 25);
179    ctx.stroke();
180    @assert pixel 50,25 == 0,255,0,255;
181  expected: green
182
183- name: 2d.path.lineTo.nextpoint
184  testing:
185  - 2d.path.lineTo.nonempty
186  code: |
187    ctx.fillStyle = '#f00';
188    ctx.fillRect(0, 0, 100, 50);
189    ctx.strokeStyle = '#0f0';
190    ctx.lineWidth = 50;
191    ctx.beginPath();
192    ctx.moveTo(-100, -100);
193    ctx.lineTo(0, 25);
194    ctx.lineTo(100, 25);
195    ctx.stroke();
196    @assert pixel 50,25 == 0,255,0,255;
197  expected: green
198
199- name: 2d.path.lineTo.nonfinite
200  desc: lineTo() with Infinity/NaN is ignored
201  testing:
202  - 2d.nonfinite
203  code: |
204    ctx.moveTo(0, 0);
205    ctx.lineTo(100, 0);
206    @nonfinite ctx.lineTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
207    ctx.lineTo(100, 50);
208    ctx.lineTo(0, 50);
209    ctx.fillStyle = '#0f0';
210    ctx.fill();
211    @assert pixel 50,25 == 0,255,0,255;
212    @assert pixel 90,45 == 0,255,0,255;
213  expected: green
214
215- name: 2d.path.lineTo.nonfinite.details
216  desc: lineTo() with Infinity/NaN for first arg still converts the second arg
217  testing:
218  - 2d.nonfinite
219  code: |
220    for (var arg1 of [Infinity, -Infinity, NaN]) {
221      var converted = false;
222      ctx.lineTo(arg1, { valueOf: function() { converted = true; return 0; } });
223      @assert converted;
224    }
225  expected: clear
226
227- name: 2d.path.quadraticCurveTo.ensuresubpath.1
228  desc: If there is no subpath, the first control point is added (and nothing is drawn
229    up to it)
230  testing:
231  - 2d.path.quadratic.empty
232  - 2d.path.ensure
233  code: |
234    ctx.fillStyle = '#0f0';
235    ctx.fillRect(0, 0, 100, 50);
236    ctx.strokeStyle = '#f00';
237    ctx.lineWidth = 50;
238    ctx.beginPath();
239    ctx.quadraticCurveTo(100, 50, 200, 50);
240    ctx.stroke();
241    @assert pixel 50,25 == 0,255,0,255;
242    @assert pixel 95,45 == 0,255,0,255; @moz-todo
243  expected: green
244
245- name: 2d.path.quadraticCurveTo.ensuresubpath.2
246  desc: If there is no subpath, the first control point is added
247  testing:
248  - 2d.path.quadratic.empty
249  - 2d.path.ensure
250  code: |
251    ctx.fillStyle = '#f00';
252    ctx.fillRect(0, 0, 100, 50);
253    ctx.strokeStyle = '#0f0';
254    ctx.lineWidth = 50;
255    ctx.beginPath();
256    ctx.quadraticCurveTo(0, 25, 100, 25);
257    ctx.stroke();
258    @assert pixel 50,25 == 0,255,0,255;
259    @assert pixel 5,45 == 0,255,0,255; @moz-todo
260  expected: green
261
262- name: 2d.path.quadraticCurveTo.basic
263  testing:
264  - 2d.path.quadratic.nonempty
265  code: |
266    ctx.fillStyle = '#f00';
267    ctx.fillRect(0, 0, 100, 50);
268    ctx.strokeStyle = '#0f0';
269    ctx.lineWidth = 50;
270    ctx.beginPath();
271    ctx.moveTo(0, 25);
272    ctx.quadraticCurveTo(100, 25, 100, 25);
273    ctx.stroke();
274    @assert pixel 50,25 == 0,255,0,255;
275  expected: green
276
277- name: 2d.path.quadraticCurveTo.shape
278  testing:
279  - 2d.path.quadratic.nonempty
280  code: |
281    ctx.fillStyle = '#f00';
282    ctx.fillRect(0, 0, 100, 50);
283    ctx.strokeStyle = '#0f0';
284    ctx.lineWidth = 55;
285    ctx.beginPath();
286    ctx.moveTo(-1000, 1050);
287    ctx.quadraticCurveTo(0, -1000, 1200, 1050);
288    ctx.stroke();
289    @assert pixel 50,25 == 0,255,0,255;
290    @assert pixel 1,1 == 0,255,0,255;
291    @assert pixel 98,1 == 0,255,0,255;
292    @assert pixel 1,48 == 0,255,0,255;
293    @assert pixel 98,48 == 0,255,0,255;
294  expected: green
295
296- name: 2d.path.quadraticCurveTo.scaled
297  testing:
298  - 2d.path.quadratic.nonempty
299  code: |
300    ctx.fillStyle = '#f00';
301    ctx.fillRect(0, 0, 100, 50);
302    ctx.scale(1000, 1000);
303    ctx.strokeStyle = '#0f0';
304    ctx.lineWidth = 0.055;
305    ctx.beginPath();
306    ctx.moveTo(-1, 1.05);
307    ctx.quadraticCurveTo(0, -1, 1.2, 1.05);
308    ctx.stroke();
309    @assert pixel 50,25 == 0,255,0,255;
310    @assert pixel 1,1 == 0,255,0,255;
311    @assert pixel 98,1 == 0,255,0,255;
312    @assert pixel 1,48 == 0,255,0,255;
313    @assert pixel 98,48 == 0,255,0,255;
314  expected: green
315
316- name: 2d.path.quadraticCurveTo.nonfinite
317  desc: quadraticCurveTo() with Infinity/NaN is ignored
318  testing:
319  - 2d.nonfinite
320  code: |
321    ctx.moveTo(0, 0);
322    ctx.lineTo(100, 0);
323    @nonfinite ctx.quadraticCurveTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
324    ctx.lineTo(100, 50);
325    ctx.lineTo(0, 50);
326    ctx.fillStyle = '#0f0';
327    ctx.fill();
328    @assert pixel 50,25 == 0,255,0,255;
329    @assert pixel 90,45 == 0,255,0,255;
330  expected: green
331
332- name: 2d.path.bezierCurveTo.ensuresubpath.1
333  desc: If there is no subpath, the first control point is added (and nothing is drawn
334    up to it)
335  testing:
336  - 2d.path.bezier.empty
337  - 2d.path.ensure
338  code: |
339    ctx.fillStyle = '#0f0';
340    ctx.fillRect(0, 0, 100, 50);
341    ctx.strokeStyle = '#f00';
342    ctx.lineWidth = 50;
343    ctx.beginPath();
344    ctx.bezierCurveTo(100, 50, 200, 50, 200, 50);
345    ctx.stroke();
346    @assert pixel 50,25 == 0,255,0,255;
347    @assert pixel 95,45 == 0,255,0,255;
348  expected: green
349
350- name: 2d.path.bezierCurveTo.ensuresubpath.2
351  desc: If there is no subpath, the first control point is added
352  testing:
353  - 2d.path.bezier.empty
354  - 2d.path.ensure
355  code: |
356    ctx.fillStyle = '#f00';
357    ctx.fillRect(0, 0, 100, 50);
358    ctx.strokeStyle = '#0f0';
359    ctx.lineWidth = 50;
360    ctx.beginPath();
361    ctx.bezierCurveTo(0, 25, 100, 25, 100, 25);
362    ctx.stroke();
363    @assert pixel 50,25 == 0,255,0,255;
364    @assert pixel 5,45 == 0,255,0,255;
365  expected: green
366
367- name: 2d.path.bezierCurveTo.basic
368  testing:
369  - 2d.path.bezier.nonempty
370  code: |
371    ctx.fillStyle = '#f00';
372    ctx.fillRect(0, 0, 100, 50);
373    ctx.strokeStyle = '#0f0';
374    ctx.lineWidth = 50;
375    ctx.beginPath();
376    ctx.moveTo(0, 25);
377    ctx.bezierCurveTo(100, 25, 100, 25, 100, 25);
378    ctx.stroke();
379    @assert pixel 50,25 == 0,255,0,255;
380  expected: green
381
382- name: 2d.path.bezierCurveTo.shape
383  testing:
384  - 2d.path.bezier.nonempty
385  code: |
386    ctx.fillStyle = '#f00';
387    ctx.fillRect(0, 0, 100, 50);
388    ctx.strokeStyle = '#0f0';
389    ctx.lineWidth = 55;
390    ctx.beginPath();
391    ctx.moveTo(-2000, 3100);
392    ctx.bezierCurveTo(-2000, -1000, 2100, -1000, 2100, 3100);
393    ctx.stroke();
394    @assert pixel 50,25 == 0,255,0,255;
395    @assert pixel 1,1 == 0,255,0,255;
396    @assert pixel 98,1 == 0,255,0,255;
397    @assert pixel 1,48 == 0,255,0,255;
398    @assert pixel 98,48 == 0,255,0,255;
399  expected: green
400
401- name: 2d.path.bezierCurveTo.scaled
402  testing:
403  - 2d.path.bezier.nonempty
404  code: |
405    ctx.fillStyle = '#f00';
406    ctx.fillRect(0, 0, 100, 50);
407    ctx.scale(1000, 1000);
408    ctx.strokeStyle = '#0f0';
409    ctx.lineWidth = 0.055;
410    ctx.beginPath();
411    ctx.moveTo(-2, 3.1);
412    ctx.bezierCurveTo(-2, -1, 2.1, -1, 2.1, 3.1);
413    ctx.stroke();
414    @assert pixel 50,25 == 0,255,0,255;
415    @assert pixel 1,1 == 0,255,0,255;
416    @assert pixel 98,1 == 0,255,0,255;
417    @assert pixel 1,48 == 0,255,0,255;
418    @assert pixel 98,48 == 0,255,0,255;
419  expected: green
420
421- name: 2d.path.bezierCurveTo.nonfinite
422  desc: bezierCurveTo() with Infinity/NaN is ignored
423  testing:
424  - 2d.nonfinite
425  code: |
426    ctx.moveTo(0, 0);
427    ctx.lineTo(100, 0);
428    @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>);
429    ctx.lineTo(100, 50);
430    ctx.lineTo(0, 50);
431    ctx.fillStyle = '#0f0';
432    ctx.fill();
433    @assert pixel 50,25 == 0,255,0,255;
434    @assert pixel 90,45 == 0,255,0,255;
435  expected: green
436
437- name: 2d.path.arcTo.ensuresubpath.1
438  desc: If there is no subpath, the first control point is added (and nothing is drawn
439    up to it)
440  testing:
441  - 2d.path.arcTo.empty
442  - 2d.path.ensure
443  code: |
444    ctx.fillStyle = '#0f0';
445    ctx.fillRect(0, 0, 100, 50);
446    ctx.lineWidth = 50;
447    ctx.strokeStyle = '#f00';
448    ctx.beginPath();
449    ctx.arcTo(100, 50, 200, 50, 0.1);
450    ctx.stroke();
451    @assert pixel 50,25 == 0,255,0,255;
452  expected: green
453
454- name: 2d.path.arcTo.ensuresubpath.2
455  desc: If there is no subpath, the first control point is added
456  testing:
457  - 2d.path.arcTo.empty
458  - 2d.path.ensure
459  code: |
460    ctx.fillStyle = '#f00';
461    ctx.fillRect(0, 0, 100, 50);
462    ctx.lineWidth = 50;
463    ctx.strokeStyle = '#0f0';
464    ctx.beginPath();
465    ctx.arcTo(0, 25, 50, 250, 0.1); // adds (x1,y1), draws nothing
466    ctx.lineTo(100, 25);
467    ctx.stroke();
468    @assert pixel 50,25 == 0,255,0,255;
469  expected: green
470
471- name: 2d.path.arcTo.coincide.1
472  desc: arcTo() has no effect if P0 = P1
473  testing:
474  - 2d.path.arcTo.coincide.01
475  code: |
476    ctx.fillStyle = '#f00';
477    ctx.fillRect(0, 0, 100, 50);
478    ctx.lineWidth = 50;
479
480    ctx.strokeStyle = '#0f0';
481    ctx.beginPath();
482    ctx.moveTo(0, 25);
483    ctx.arcTo(0, 25, 50, 1000, 1);
484    ctx.lineTo(100, 25);
485    ctx.stroke();
486
487    ctx.strokeStyle = '#f00';
488    ctx.beginPath();
489    ctx.moveTo(50, 25);
490    ctx.arcTo(50, 25, 100, 25, 1);
491    ctx.stroke();
492
493    @assert pixel 50,1 == 0,255,0,255;
494    @assert pixel 50,25 == 0,255,0,255;
495    @assert pixel 50,48 == 0,255,0,255;
496  expected: green
497
498- name: 2d.path.arcTo.coincide.2
499  desc: arcTo() draws a straight line to P1 if P1 = P2
500  testing:
501  - 2d.path.arcTo.coincide.12
502  code: |
503    ctx.fillStyle = '#f00';
504    ctx.fillRect(0, 0, 100, 50);
505    ctx.lineWidth = 50;
506    ctx.strokeStyle = '#0f0';
507    ctx.beginPath();
508    ctx.moveTo(0, 25);
509    ctx.arcTo(100, 25, 100, 25, 1);
510    ctx.stroke();
511
512    @assert pixel 50,25 == 0,255,0,255;
513  expected: green
514
515- name: 2d.path.arcTo.collinear.1
516  desc: arcTo() with all points on a line, and P1 between P0/P2, draws a straight
517    line to P1
518  testing:
519  - 2d.path.arcTo.collinear
520  code: |
521    ctx.fillStyle = '#f00';
522    ctx.fillRect(0, 0, 100, 50);
523    ctx.lineWidth = 50;
524
525    ctx.strokeStyle = '#0f0';
526    ctx.beginPath();
527    ctx.moveTo(0, 25);
528    ctx.arcTo(100, 25, 200, 25, 1);
529    ctx.stroke();
530
531    ctx.strokeStyle = '#f00';
532    ctx.beginPath();
533    ctx.moveTo(-100, 25);
534    ctx.arcTo(0, 25, 100, 25, 1);
535    ctx.stroke();
536
537    @assert pixel 50,25 == 0,255,0,255;
538  expected: green
539
540- name: 2d.path.arcTo.collinear.2
541  desc: arcTo() with all points on a line, and P2 between P0/P1, draws a straight
542    line to P1
543  testing:
544  - 2d.path.arcTo.collinear
545  code: |
546    ctx.fillStyle = '#f00';
547    ctx.fillRect(0, 0, 100, 50);
548    ctx.lineWidth = 50;
549
550    ctx.strokeStyle = '#0f0';
551    ctx.beginPath();
552    ctx.moveTo(0, 25);
553    ctx.arcTo(100, 25, 10, 25, 1);
554    ctx.stroke();
555
556    ctx.strokeStyle = '#f00';
557    ctx.beginPath();
558    ctx.moveTo(100, 25);
559    ctx.arcTo(200, 25, 110, 25, 1);
560    ctx.stroke();
561
562    @assert pixel 50,25 == 0,255,0,255;
563  expected: green
564
565- name: 2d.path.arcTo.collinear.3
566  desc: arcTo() with all points on a line, and P0 between P1/P2, draws a straight
567    line to P1
568  testing:
569  - 2d.path.arcTo.collinear
570  code: |
571    ctx.fillStyle = '#f00';
572    ctx.fillRect(0, 0, 100, 50);
573    ctx.lineWidth = 50;
574
575    ctx.strokeStyle = '#0f0';
576    ctx.beginPath();
577    ctx.moveTo(0, 25);
578    ctx.arcTo(100, 25, -100, 25, 1);
579    ctx.stroke();
580
581    ctx.strokeStyle = '#f00';
582    ctx.beginPath();
583    ctx.moveTo(100, 25);
584    ctx.arcTo(200, 25, 0, 25, 1);
585    ctx.stroke();
586
587    ctx.beginPath();
588    ctx.moveTo(-100, 25);
589    ctx.arcTo(0, 25, -200, 25, 1);
590    ctx.stroke();
591
592    @assert pixel 50,25 == 0,255,0,255;
593  expected: green
594
595- name: 2d.path.arcTo.shape.curve1
596  desc: arcTo() curves in the right kind of shape
597  testing:
598  - 2d.path.arcTo.shape
599  code: |
600    var tol = 1.5; // tolerance to avoid antialiasing artifacts
601
602    ctx.fillStyle = '#0f0';
603    ctx.fillRect(0, 0, 100, 50);
604
605    ctx.strokeStyle = '#f00';
606    ctx.lineWidth = 10;
607    ctx.beginPath();
608    ctx.moveTo(10, 25);
609    ctx.arcTo(75, 25, 75, 60, 20);
610    ctx.stroke();
611
612    ctx.fillStyle = '#0f0';
613    ctx.beginPath();
614    ctx.rect(10, 20, 45, 10);
615    ctx.moveTo(80, 45);
616    ctx.arc(55, 45, 25+tol, 0, -Math.PI/2, true);
617    ctx.arc(55, 45, 15-tol, -Math.PI/2, 0, false);
618    ctx.fill();
619
620    @assert pixel 50,25 == 0,255,0,255;
621    @assert pixel 55,19 == 0,255,0,255;
622    @assert pixel 55,20 == 0,255,0,255;
623    @assert pixel 55,21 == 0,255,0,255;
624    @assert pixel 64,22 == 0,255,0,255;
625    @assert pixel 65,21 == 0,255,0,255;
626    @assert pixel 72,28 == 0,255,0,255;
627    @assert pixel 73,27 == 0,255,0,255;
628    @assert pixel 78,36 == 0,255,0,255;
629    @assert pixel 79,35 == 0,255,0,255;
630    @assert pixel 80,44 == 0,255,0,255;
631    @assert pixel 80,45 == 0,255,0,255;
632    @assert pixel 80,46 == 0,255,0,255;
633    @assert pixel 65,45 == 0,255,0,255;
634  expected: green
635
636- name: 2d.path.arcTo.shape.curve2
637  desc: arcTo() curves in the right kind of shape
638  testing:
639  - 2d.path.arcTo.shape
640  code: |
641    var tol = 1.5; // tolerance to avoid antialiasing artifacts
642
643    ctx.fillStyle = '#0f0';
644    ctx.fillRect(0, 0, 100, 50);
645
646    ctx.fillStyle = '#f00';
647    ctx.beginPath();
648    ctx.rect(10, 20, 45, 10);
649    ctx.moveTo(80, 45);
650    ctx.arc(55, 45, 25-tol, 0, -Math.PI/2, true);
651    ctx.arc(55, 45, 15+tol, -Math.PI/2, 0, false);
652    ctx.fill();
653
654    ctx.strokeStyle = '#0f0';
655    ctx.lineWidth = 10;
656    ctx.beginPath();
657    ctx.moveTo(10, 25);
658    ctx.arcTo(75, 25, 75, 60, 20);
659    ctx.stroke();
660
661    @assert pixel 50,25 == 0,255,0,255;
662    @assert pixel 55,19 == 0,255,0,255;
663    @assert pixel 55,20 == 0,255,0,255;
664    @assert pixel 55,21 == 0,255,0,255;
665    @assert pixel 64,22 == 0,255,0,255;
666    @assert pixel 65,21 == 0,255,0,255;
667    @assert pixel 72,28 == 0,255,0,255;
668    @assert pixel 73,27 == 0,255,0,255;
669    @assert pixel 78,36 == 0,255,0,255;
670    @assert pixel 79,35 == 0,255,0,255;
671    @assert pixel 80,44 == 0,255,0,255;
672    @assert pixel 80,45 == 0,255,0,255;
673    @assert pixel 80,46 == 0,255,0,255;
674  expected: green
675
676- name: 2d.path.arcTo.shape.start
677  desc: arcTo() draws a straight line from P0 to P1
678  testing:
679  - 2d.path.arcTo.shape
680  code: |
681    ctx.fillStyle = '#f00';
682    ctx.fillRect(0, 0, 100, 50);
683    ctx.strokeStyle = '#0f0';
684    ctx.lineWidth = 50;
685    ctx.beginPath();
686    ctx.moveTo(0, 25);
687    ctx.arcTo(200, 25, 200, 50, 10);
688    ctx.stroke();
689
690    @assert pixel 1,1 == 0,255,0,255;
691    @assert pixel 1,48 == 0,255,0,255;
692    @assert pixel 50,25 == 0,255,0,255;
693    @assert pixel 98,1 == 0,255,0,255;
694    @assert pixel 98,48 == 0,255,0,255;
695  expected: green
696
697- name: 2d.path.arcTo.shape.end
698  desc: arcTo() does not draw anything from P1 to P2
699  testing:
700  - 2d.path.arcTo.shape
701  code: |
702    ctx.fillStyle = '#0f0';
703    ctx.fillRect(0, 0, 100, 50);
704    ctx.strokeStyle = '#f00';
705    ctx.lineWidth = 50;
706    ctx.beginPath();
707    ctx.moveTo(-100, -100);
708    ctx.arcTo(-100, 25, 200, 25, 10);
709    ctx.stroke();
710
711    @assert pixel 1,1 == 0,255,0,255;
712    @assert pixel 1,48 == 0,255,0,255;
713    @assert pixel 50,25 == 0,255,0,255;
714    @assert pixel 98,1 == 0,255,0,255;
715    @assert pixel 98,48 == 0,255,0,255;
716  expected: green
717
718- name: 2d.path.arcTo.negative
719  desc: arcTo() with negative radius throws an exception
720  testing:
721  - 2d.path.arcTo.negative
722  code: |
723    @assert throws INDEX_SIZE_ERR ctx.arcTo(0, 0, 0, 0, -1);
724    var path = new Path2D();
725    @assert throws INDEX_SIZE_ERR path.arcTo(10, 10, 20, 20, -5);
726
727- name: 2d.path.arcTo.zero.1
728  desc: arcTo() with zero radius draws a straight line from P0 to P1
729  testing:
730  - 2d.path.arcTo.zeroradius
731  code: |
732    ctx.fillStyle = '#f00';
733    ctx.fillRect(0, 0, 100, 50);
734    ctx.lineWidth = 50;
735
736    ctx.strokeStyle = '#0f0';
737    ctx.beginPath();
738    ctx.moveTo(0, 25);
739    ctx.arcTo(100, 25, 100, 100, 0);
740    ctx.stroke();
741
742    ctx.strokeStyle = '#f00';
743    ctx.beginPath();
744    ctx.moveTo(0, -25);
745    ctx.arcTo(50, -25, 50, 50, 0);
746    ctx.stroke();
747
748    @assert pixel 50,25 == 0,255,0,255;
749  expected: green
750
751- name: 2d.path.arcTo.zero.2
752  desc: arcTo() with zero radius draws a straight line from P0 to P1, even when all
753    points are collinear
754  testing:
755  - 2d.path.arcTo.zeroradius
756  code: |
757    ctx.fillStyle = '#f00';
758    ctx.fillRect(0, 0, 100, 50);
759    ctx.lineWidth = 50;
760
761    ctx.strokeStyle = '#0f0';
762    ctx.beginPath();
763    ctx.moveTo(0, 25);
764    ctx.arcTo(100, 25, -100, 25, 0);
765    ctx.stroke();
766
767    ctx.strokeStyle = '#f00';
768    ctx.beginPath();
769    ctx.moveTo(100, 25);
770    ctx.arcTo(200, 25, 50, 25, 0);
771    ctx.stroke();
772
773    @assert pixel 50,25 == 0,255,0,255;
774  expected: green
775
776- name: 2d.path.arcTo.transformation
777  desc: arcTo joins up to the last subpath point correctly
778  code: |
779    ctx.fillStyle = '#f00';
780    ctx.fillRect(0, 0, 100, 50);
781
782    ctx.fillStyle = '#0f0';
783    ctx.beginPath();
784    ctx.moveTo(0, 50);
785    ctx.translate(100, 0);
786    ctx.arcTo(50, 50, 50, 0, 50);
787    ctx.lineTo(-100, 0);
788    ctx.fill();
789
790    @assert pixel 0,0 == 0,255,0,255;
791    @assert pixel 50,0 == 0,255,0,255;
792    @assert pixel 99,0 == 0,255,0,255;
793    @assert pixel 0,25 == 0,255,0,255;
794    @assert pixel 50,25 == 0,255,0,255;
795    @assert pixel 99,25 == 0,255,0,255;
796    @assert pixel 0,49 == 0,255,0,255;
797    @assert pixel 50,49 == 0,255,0,255;
798    @assert pixel 99,49 == 0,255,0,255;
799  expected: green
800
801- name: 2d.path.arcTo.scale
802  desc: arcTo scales the curve, not just the control points
803  code: |
804    ctx.fillStyle = '#f00';
805    ctx.fillRect(0, 0, 100, 50);
806
807    ctx.fillStyle = '#0f0';
808    ctx.beginPath();
809    ctx.moveTo(0, 50);
810    ctx.translate(100, 0);
811    ctx.scale(0.1, 1);
812    ctx.arcTo(50, 50, 50, 0, 50);
813    ctx.lineTo(-1000, 0);
814    ctx.fill();
815
816    @assert pixel 0,0 == 0,255,0,255;
817    @assert pixel 50,0 == 0,255,0,255;
818    @assert pixel 99,0 == 0,255,0,255;
819    @assert pixel 0,25 == 0,255,0,255;
820    @assert pixel 50,25 == 0,255,0,255;
821    @assert pixel 99,25 == 0,255,0,255;
822    @assert pixel 0,49 == 0,255,0,255;
823    @assert pixel 50,49 == 0,255,0,255;
824    @assert pixel 99,49 == 0,255,0,255;
825  expected: green
826
827- name: 2d.path.arcTo.nonfinite
828  desc: arcTo() with Infinity/NaN is ignored
829  testing:
830  - 2d.nonfinite
831  code: |
832    ctx.moveTo(0, 0);
833    ctx.lineTo(100, 0);
834    @nonfinite ctx.arcTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
835    ctx.lineTo(100, 50);
836    ctx.lineTo(0, 50);
837    ctx.fillStyle = '#0f0';
838    ctx.fill();
839    @assert pixel 50,25 == 0,255,0,255;
840    @assert pixel 90,45 == 0,255,0,255;
841  expected: green
842
843
844- name: 2d.path.arc.empty
845  desc: arc() with an empty path does not draw a straight line to the start point
846  testing:
847  - 2d.path.arc.nonempty
848  code: |
849    ctx.fillStyle = '#0f0';
850    ctx.fillRect(0, 0, 100, 50);
851    ctx.lineWidth = 50;
852    ctx.strokeStyle = '#f00';
853    ctx.beginPath();
854    ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
855    ctx.stroke();
856    @assert pixel 50,25 == 0,255,0,255;
857  expected: green
858
859- name: 2d.path.arc.nonempty
860  desc: arc() with a non-empty path does draw a straight line to the start point
861  testing:
862  - 2d.path.arc.nonempty
863  code: |
864    ctx.fillStyle = '#f00';
865    ctx.fillRect(0, 0, 100, 50);
866    ctx.lineWidth = 50;
867    ctx.strokeStyle = '#0f0';
868    ctx.beginPath();
869    ctx.moveTo(0, 25);
870    ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
871    ctx.stroke();
872    @assert pixel 50,25 == 0,255,0,255;
873  expected: green
874
875- name: 2d.path.arc.end
876  desc: arc() adds the end point of the arc to the subpath
877  testing:
878  - 2d.path.arc.draw
879  code: |
880    ctx.fillStyle = '#f00';
881    ctx.fillRect(0, 0, 100, 50);
882    ctx.lineWidth = 50;
883    ctx.strokeStyle = '#0f0';
884    ctx.beginPath();
885    ctx.moveTo(-100, 0);
886    ctx.arc(-100, 0, 25, -Math.PI/2, Math.PI/2, true);
887    ctx.lineTo(100, 25);
888    ctx.stroke();
889    @assert pixel 50,25 == 0,255,0,255;
890  expected: green
891
892- name: 2d.path.arc.default
893  desc: arc() with missing last argument defaults to clockwise
894  testing:
895  - 2d.path.arc.omitted
896  code: |
897    ctx.fillStyle = '#0f0';
898    ctx.fillRect(0, 0, 100, 50);
899    ctx.fillStyle = '#f00';
900    ctx.beginPath();
901    ctx.moveTo(100, 0);
902    ctx.arc(100, 0, 150, -Math.PI, Math.PI/2);
903    ctx.fill();
904    @assert pixel 50,25 == 0,255,0,255;
905  expected: green
906
907- name: 2d.path.arc.angle.1
908  desc: arc() draws pi/2 .. -pi anticlockwise correctly
909  testing:
910  - 2d.path.arc.draw
911  code: |
912    ctx.fillStyle = '#0f0';
913    ctx.fillRect(0, 0, 100, 50);
914    ctx.fillStyle = '#f00';
915    ctx.beginPath();
916    ctx.moveTo(100, 0);
917    ctx.arc(100, 0, 150, Math.PI/2, -Math.PI, true);
918    ctx.fill();
919    @assert pixel 50,25 == 0,255,0,255;
920  expected: green
921
922- name: 2d.path.arc.angle.2
923  desc: arc() draws -3pi/2 .. -pi anticlockwise correctly
924  testing:
925  - 2d.path.arc.draw
926  code: |
927    ctx.fillStyle = '#0f0';
928    ctx.fillRect(0, 0, 100, 50);
929    ctx.fillStyle = '#f00';
930    ctx.beginPath();
931    ctx.moveTo(100, 0);
932    ctx.arc(100, 0, 150, -3*Math.PI/2, -Math.PI, true);
933    ctx.fill();
934    @assert pixel 50,25 == 0,255,0,255;
935  expected: green
936
937- name: 2d.path.arc.angle.3
938  desc: arc() wraps angles mod 2pi when anticlockwise and end > start+2pi
939  testing:
940  - 2d.path.arc.draw
941  code: |
942    ctx.fillStyle = '#0f0';
943    ctx.fillRect(0, 0, 100, 50);
944    ctx.fillStyle = '#f00';
945    ctx.beginPath();
946    ctx.moveTo(100, 0);
947    ctx.arc(100, 0, 150, (512+1/2)*Math.PI, (1024-1)*Math.PI, true);
948    ctx.fill();
949    @assert pixel 50,25 == 0,255,0,255;
950  expected: green
951
952- name: 2d.path.arc.angle.4
953  desc: arc() draws a full circle when clockwise and end > start+2pi
954  testing:
955  - 2d.path.arc.draw
956  code: |
957    ctx.fillStyle = '#f00';
958    ctx.fillRect(0, 0, 100, 50);
959    ctx.fillStyle = '#0f0';
960    ctx.beginPath();
961    ctx.moveTo(50, 25);
962    ctx.arc(50, 25, 60, (512+1/2)*Math.PI, (1024-1)*Math.PI, false);
963    ctx.fill();
964    @assert pixel 1,1 == 0,255,0,255;
965    @assert pixel 98,1 == 0,255,0,255;
966    @assert pixel 1,48 == 0,255,0,255;
967    @assert pixel 98,48 == 0,255,0,255;
968  expected: green
969
970- name: 2d.path.arc.angle.5
971  desc: arc() wraps angles mod 2pi when clockwise and start > end+2pi
972  testing:
973  - 2d.path.arc.draw
974  code: |
975    ctx.fillStyle = '#0f0';
976    ctx.fillRect(0, 0, 100, 50);
977    ctx.fillStyle = '#f00';
978    ctx.beginPath();
979    ctx.moveTo(100, 0);
980    ctx.arc(100, 0, 150, (1024-1)*Math.PI, (512+1/2)*Math.PI, false);
981    ctx.fill();
982    @assert pixel 50,25 == 0,255,0,255;
983  expected: green
984
985- name: 2d.path.arc.angle.6
986  desc: arc() draws a full circle when anticlockwise and start > end+2pi
987  testing:
988  - 2d.path.arc.draw
989  code: |
990    ctx.fillStyle = '#f00';
991    ctx.fillRect(0, 0, 100, 50);
992    ctx.fillStyle = '#0f0';
993    ctx.beginPath();
994    ctx.moveTo(50, 25);
995    ctx.arc(50, 25, 60, (1024-1)*Math.PI, (512+1/2)*Math.PI, true);
996    ctx.fill();
997    @assert pixel 1,1 == 0,255,0,255;
998    @assert pixel 98,1 == 0,255,0,255;
999    @assert pixel 1,48 == 0,255,0,255;
1000    @assert pixel 98,48 == 0,255,0,255;
1001  expected: green
1002
1003- name: 2d.path.arc.zero.1
1004  desc: arc() draws nothing when startAngle = endAngle and anticlockwise
1005  testing:
1006  - 2d.path.arc.draw
1007  code: |
1008    ctx.fillStyle = '#0f0';
1009    ctx.fillRect(0, 0, 100, 50);
1010    ctx.strokeStyle = '#f00';
1011    ctx.lineWidth = 100;
1012    ctx.beginPath();
1013    ctx.arc(50, 25, 50, 0, 0, true);
1014    ctx.stroke();
1015    @assert pixel 50,20 == 0,255,0,255;
1016  expected: green
1017
1018- name: 2d.path.arc.zero.2
1019  desc: arc() draws nothing when startAngle = endAngle and clockwise
1020  testing:
1021  - 2d.path.arc.draw
1022  code: |
1023    ctx.fillStyle = '#0f0';
1024    ctx.fillRect(0, 0, 100, 50);
1025    ctx.strokeStyle = '#f00';
1026    ctx.lineWidth = 100;
1027    ctx.beginPath();
1028    ctx.arc(50, 25, 50, 0, 0, false);
1029    ctx.stroke();
1030    @assert pixel 50,20 == 0,255,0,255;
1031  expected: green
1032
1033- name: 2d.path.arc.twopie.1
1034  desc: arc() draws nothing when end = start + 2pi-e and anticlockwise
1035  testing:
1036  - 2d.path.arc.draw
1037  code: |
1038    ctx.fillStyle = '#0f0';
1039    ctx.fillRect(0, 0, 100, 50);
1040    ctx.strokeStyle = '#f00';
1041    ctx.lineWidth = 100;
1042    ctx.beginPath();
1043    ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, true);
1044    ctx.stroke();
1045    @assert pixel 50,20 == 0,255,0,255;
1046  expected: green
1047
1048- name: 2d.path.arc.twopie.2
1049  desc: arc() draws a full circle when end = start + 2pi-e and clockwise
1050  testing:
1051  - 2d.path.arc.draw
1052  code: |
1053    ctx.fillStyle = '#f00';
1054    ctx.fillRect(0, 0, 100, 50);
1055    ctx.strokeStyle = '#0f0';
1056    ctx.lineWidth = 100;
1057    ctx.beginPath();
1058    ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, false);
1059    ctx.stroke();
1060    @assert pixel 50,20 == 0,255,0,255;
1061  expected: green
1062
1063- name: 2d.path.arc.twopie.3
1064  desc: arc() draws a full circle when end = start + 2pi+e and anticlockwise
1065  testing:
1066  - 2d.path.arc.draw
1067  code: |
1068    ctx.fillStyle = '#f00';
1069    ctx.fillRect(0, 0, 100, 50);
1070    ctx.strokeStyle = '#0f0';
1071    ctx.lineWidth = 100;
1072    ctx.beginPath();
1073    ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, true);
1074    ctx.stroke();
1075    @assert pixel 50,20 == 0,255,0,255;
1076  expected: green
1077
1078- name: 2d.path.arc.twopie.4
1079  desc: arc() draws nothing when end = start + 2pi+e and clockwise
1080  testing:
1081  - 2d.path.arc.draw
1082  code: |
1083    ctx.fillStyle = '#f00';
1084    ctx.fillRect(0, 0, 100, 50);
1085    ctx.strokeStyle = '#0f0';
1086    ctx.lineWidth = 100;
1087    ctx.beginPath();
1088    ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, false);
1089    ctx.stroke();
1090    @assert pixel 50,20 == 0,255,0,255;
1091  expected: green
1092
1093- name: 2d.path.arc.shape.1
1094  desc: arc() from 0 to pi does not draw anything in the wrong half
1095  testing:
1096  - 2d.path.arc.draw
1097  code: |
1098    ctx.fillStyle = '#0f0';
1099    ctx.fillRect(0, 0, 100, 50);
1100    ctx.lineWidth = 50;
1101    ctx.strokeStyle = '#f00';
1102    ctx.beginPath();
1103    ctx.arc(50, 50, 50, 0, Math.PI, false);
1104    ctx.stroke();
1105    @assert pixel 50,25 == 0,255,0,255;
1106    @assert pixel 1,1 == 0,255,0,255;
1107    @assert pixel 98,1 == 0,255,0,255;
1108    @assert pixel 1,48 == 0,255,0,255;
1109    @assert pixel 20,48 == 0,255,0,255;
1110    @assert pixel 98,48 == 0,255,0,255;
1111  expected: green
1112
1113- name: 2d.path.arc.shape.2
1114  desc: arc() from 0 to pi draws stuff in the right half
1115  testing:
1116  - 2d.path.arc.draw
1117  code: |
1118    ctx.fillStyle = '#f00';
1119    ctx.fillRect(0, 0, 100, 50);
1120    ctx.lineWidth = 100;
1121    ctx.strokeStyle = '#0f0';
1122    ctx.beginPath();
1123    ctx.arc(50, 50, 50, 0, Math.PI, true);
1124    ctx.stroke();
1125    @assert pixel 50,25 == 0,255,0,255;
1126    @assert pixel 1,1 == 0,255,0,255;
1127    @assert pixel 98,1 == 0,255,0,255;
1128    @assert pixel 1,48 == 0,255,0,255;
1129    @assert pixel 20,48 == 0,255,0,255;
1130    @assert pixel 98,48 == 0,255,0,255;
1131  expected: green
1132
1133- name: 2d.path.arc.shape.3
1134  desc: arc() from 0 to -pi/2 does not draw anything in the wrong quadrant
1135  testing:
1136  - 2d.path.arc.draw
1137  code: |
1138    ctx.fillStyle = '#0f0';
1139    ctx.fillRect(0, 0, 100, 50);
1140    ctx.lineWidth = 100;
1141    ctx.strokeStyle = '#f00';
1142    ctx.beginPath();
1143    ctx.arc(0, 50, 50, 0, -Math.PI/2, false);
1144    ctx.stroke();
1145    @assert pixel 50,25 == 0,255,0,255;
1146    @assert pixel 1,1 == 0,255,0,255;
1147    @assert pixel 98,1 == 0,255,0,255;
1148    @assert pixel 1,48 == 0,255,0,255; @moz-todo
1149    @assert pixel 98,48 == 0,255,0,255;
1150  expected: green
1151
1152- name: 2d.path.arc.shape.4
1153  desc: arc() from 0 to -pi/2 draws stuff in the right quadrant
1154  testing:
1155  - 2d.path.arc.draw
1156  code: |
1157    ctx.fillStyle = '#f00';
1158    ctx.fillRect(0, 0, 100, 50);
1159    ctx.lineWidth = 150;
1160    ctx.strokeStyle = '#0f0';
1161    ctx.beginPath();
1162    ctx.arc(-50, 50, 100, 0, -Math.PI/2, true);
1163    ctx.stroke();
1164    @assert pixel 50,25 == 0,255,0,255;
1165    @assert pixel 1,1 == 0,255,0,255;
1166    @assert pixel 98,1 == 0,255,0,255;
1167    @assert pixel 1,48 == 0,255,0,255;
1168    @assert pixel 98,48 == 0,255,0,255;
1169  expected: green
1170
1171- name: 2d.path.arc.shape.5
1172  desc: arc() from 0 to 5pi does not draw crazy things
1173  testing:
1174  - 2d.path.arc.draw
1175  code: |
1176    ctx.fillStyle = '#0f0';
1177    ctx.fillRect(0, 0, 100, 50);
1178    ctx.lineWidth = 200;
1179    ctx.strokeStyle = '#f00';
1180    ctx.beginPath();
1181    ctx.arc(300, 0, 100, 0, 5*Math.PI, false);
1182    ctx.stroke();
1183    @assert pixel 50,25 == 0,255,0,255;
1184    @assert pixel 1,1 == 0,255,0,255;
1185    @assert pixel 98,1 == 0,255,0,255;
1186    @assert pixel 1,48 == 0,255,0,255;
1187    @assert pixel 98,48 == 0,255,0,255;
1188  expected: green
1189
1190- name: 2d.path.arc.selfintersect.1
1191  desc: arc() with lineWidth > 2*radius is drawn sensibly
1192  testing:
1193  - 2d.path.arc.draw
1194  code: |
1195    ctx.fillStyle = '#0f0';
1196    ctx.fillRect(0, 0, 100, 50);
1197    ctx.lineWidth = 200;
1198    ctx.strokeStyle = '#f00';
1199    ctx.beginPath();
1200    ctx.arc(100, 50, 25, 0, -Math.PI/2, true);
1201    ctx.stroke();
1202    ctx.beginPath();
1203    ctx.arc(0, 0, 25, 0, -Math.PI/2, true);
1204    ctx.stroke();
1205    @assert pixel 1,1 == 0,255,0,255; @moz-todo
1206    @assert pixel 50,25 == 0,255,0,255;
1207  expected: green
1208
1209- name: 2d.path.arc.selfintersect.2
1210  desc: arc() with lineWidth > 2*radius is drawn sensibly
1211  testing:
1212  - 2d.path.arc.draw
1213  code: |
1214    ctx.fillStyle = '#f00';
1215    ctx.fillRect(0, 0, 100, 50);
1216    ctx.lineWidth = 180;
1217    ctx.strokeStyle = '#0f0';
1218    ctx.beginPath();
1219    ctx.arc(-50, 50, 25, 0, -Math.PI/2, true);
1220    ctx.stroke();
1221    ctx.beginPath();
1222    ctx.arc(100, 0, 25, 0, -Math.PI/2, true);
1223    ctx.stroke();
1224    @assert pixel 50,25 == 0,255,0,255;
1225    @assert pixel 90,10 == 0,255,0,255;
1226    @assert pixel 97,1 == 0,255,0,255;
1227    @assert pixel 97,2 == 0,255,0,255;
1228    @assert pixel 97,3 == 0,255,0,255;
1229    @assert pixel 2,48 == 0,255,0,255;
1230  expected: green
1231
1232- name: 2d.path.arc.negative
1233  desc: arc() with negative radius throws INDEX_SIZE_ERR
1234  testing:
1235  - 2d.path.arc.negative
1236  code: |
1237    @assert throws INDEX_SIZE_ERR ctx.arc(0, 0, -1, 0, 0, true);
1238    var path = new Path2D();
1239    @assert throws INDEX_SIZE_ERR path.arc(10, 10, -5, 0, 1, false);
1240
1241- name: 2d.path.arc.zeroradius
1242  desc: arc() with zero radius draws a line to the start point
1243  testing:
1244  - 2d.path.arc.zero
1245  code: |
1246    ctx.fillStyle = '#f00'
1247    ctx.fillRect(0, 0, 100, 50);
1248    ctx.lineWidth = 50;
1249    ctx.strokeStyle = '#0f0';
1250    ctx.beginPath();
1251    ctx.moveTo(0, 25);
1252    ctx.arc(200, 25, 0, 0, Math.PI, true);
1253    ctx.stroke();
1254    @assert pixel 50,25 == 0,255,0,255;
1255  expected: green
1256
1257- name: 2d.path.arc.scale.1
1258  desc: Non-uniformly scaled arcs are the right shape
1259  testing:
1260  - 2d.path.transformation
1261  code: |
1262    ctx.fillStyle = '#f00';
1263    ctx.fillRect(0, 0, 100, 50);
1264    ctx.scale(2, 0.5);
1265    ctx.fillStyle = '#0f0';
1266    ctx.beginPath();
1267    ctx.arc(25, 50, 56, 0, 2*Math.PI, false);
1268    ctx.fill();
1269    ctx.fillStyle = '#f00';
1270    ctx.beginPath();
1271    ctx.moveTo(-25, 50);
1272    ctx.arc(-25, 50, 24, 0, 2*Math.PI, false);
1273    ctx.moveTo(75, 50);
1274    ctx.arc(75, 50, 24, 0, 2*Math.PI, false);
1275    ctx.moveTo(25, -25);
1276    ctx.arc(25, -25, 24, 0, 2*Math.PI, false);
1277    ctx.moveTo(25, 125);
1278    ctx.arc(25, 125, 24, 0, 2*Math.PI, false);
1279    ctx.fill();
1280
1281    @assert pixel 0,0 == 0,255,0,255;
1282    @assert pixel 50,0 == 0,255,0,255;
1283    @assert pixel 99,0 == 0,255,0,255;
1284    @assert pixel 0,25 == 0,255,0,255;
1285    @assert pixel 50,25 == 0,255,0,255;
1286    @assert pixel 99,25 == 0,255,0,255;
1287    @assert pixel 0,49 == 0,255,0,255;
1288    @assert pixel 50,49 == 0,255,0,255;
1289    @assert pixel 99,49 == 0,255,0,255;
1290  expected: green
1291
1292- name: 2d.path.arc.scale.2
1293  desc: Highly scaled arcs are the right shape
1294  testing:
1295  - 2d.path.arc.draw
1296  code: |
1297    ctx.fillStyle = '#f00';
1298    ctx.fillRect(0, 0, 100, 50);
1299    ctx.scale(100, 100);
1300    ctx.strokeStyle = '#0f0';
1301    ctx.lineWidth = 1.2;
1302    ctx.beginPath();
1303    ctx.arc(0, 0, 0.6, 0, Math.PI/2, false);
1304    ctx.stroke();
1305
1306    @assert pixel 1,1 == 0,255,0,255;
1307    @assert pixel 50,1 == 0,255,0,255;
1308    @assert pixel 98,1 == 0,255,0,255;
1309    @assert pixel 1,25 == 0,255,0,255;
1310    @assert pixel 50,25 == 0,255,0,255;
1311    @assert pixel 98,25 == 0,255,0,255;
1312    @assert pixel 1,48 == 0,255,0,255;
1313    @assert pixel 50,48 == 0,255,0,255;
1314    @assert pixel 98,48 == 0,255,0,255;
1315  expected: green
1316
1317- name: 2d.path.arc.nonfinite
1318  desc: arc() with Infinity/NaN is ignored
1319  testing:
1320  - 2d.nonfinite
1321  code: |
1322    ctx.fillStyle = '#f00';
1323    ctx.fillRect(0, 0, 100, 50);
1324    ctx.moveTo(0, 0);
1325    ctx.lineTo(100, 0);
1326    @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>);
1327    ctx.lineTo(100, 50);
1328    ctx.lineTo(0, 50);
1329    ctx.fillStyle = '#0f0';
1330    ctx.fill();
1331    @assert pixel 50,25 == 0,255,0,255;
1332    @assert pixel 90,45 == 0,255,0,255;
1333  expected: green
1334
1335
1336- name: 2d.path.rect.basic
1337  testing:
1338  - 2d.path.rect.subpath
1339  code: |
1340    ctx.fillStyle = '#f00';
1341    ctx.fillRect(0, 0, 100, 50);
1342    ctx.fillStyle = '#0f0';
1343    ctx.rect(0, 0, 100, 50);
1344    ctx.fill();
1345    @assert pixel 50,25 == 0,255,0,255;
1346  expected: green
1347
1348- name: 2d.path.rect.newsubpath
1349  testing:
1350  - 2d.path.rect.subpath
1351  code: |
1352    ctx.fillStyle = '#0f0';
1353    ctx.fillRect(0, 0, 100, 50);
1354    ctx.beginPath();
1355    ctx.strokeStyle = '#f00';
1356    ctx.lineWidth = 50;
1357    ctx.moveTo(-100, 25);
1358    ctx.lineTo(-50, 25);
1359    ctx.rect(200, 25, 1, 1);
1360    ctx.stroke();
1361    @assert pixel 50,25 == 0,255,0,255;
1362  expected: green
1363
1364- name: 2d.path.rect.closed
1365  testing:
1366  - 2d.path.rect.closed
1367  code: |
1368    ctx.fillStyle = '#f00';
1369    ctx.fillRect(0, 0, 100, 50);
1370    ctx.strokeStyle = '#0f0';
1371    ctx.lineWidth = 200;
1372    ctx.lineJoin = 'miter';
1373    ctx.rect(100, 50, 100, 100);
1374    ctx.stroke();
1375    @assert pixel 50,25 == 0,255,0,255;
1376  expected: green
1377
1378- name: 2d.path.rect.end.1
1379  testing:
1380  - 2d.path.rect.newsubpath
1381  code: |
1382    ctx.fillStyle = '#f00';
1383    ctx.fillRect(0, 0, 100, 50);
1384    ctx.strokeStyle = '#0f0';
1385    ctx.lineWidth = 100;
1386    ctx.rect(200, 100, 400, 1000);
1387    ctx.lineTo(-2000, -1000);
1388    ctx.stroke();
1389    @assert pixel 50,25 == 0,255,0,255;
1390  expected: green
1391
1392- name: 2d.path.rect.end.2
1393  testing:
1394  - 2d.path.rect.newsubpath
1395  code: |
1396    ctx.fillStyle = '#f00';
1397    ctx.fillRect(0, 0, 100, 50);
1398    ctx.strokeStyle = '#0f0';
1399    ctx.lineWidth = 450;
1400    ctx.lineCap = 'round';
1401    ctx.lineJoin = 'bevel';
1402    ctx.rect(150, 150, 2000, 2000);
1403    ctx.lineTo(160, 160);
1404    ctx.stroke();
1405    @assert pixel 1,1 == 0,255,0,255;
1406    @assert pixel 98,1 == 0,255,0,255;
1407    @assert pixel 1,48 == 0,255,0,255;
1408    @assert pixel 98,48 == 0,255,0,255;
1409  expected: green
1410
1411- name: 2d.path.rect.zero.1
1412  testing:
1413  - 2d.path.rect.subpath
1414  code: |
1415    ctx.fillStyle = '#f00';
1416    ctx.fillRect(0, 0, 100, 50);
1417    ctx.strokeStyle = '#0f0';
1418    ctx.lineWidth = 100;
1419    ctx.beginPath();
1420    ctx.rect(0, 50, 100, 0);
1421    ctx.stroke();
1422    @assert pixel 50,25 == 0,255,0,255;
1423  expected: green
1424
1425- name: 2d.path.rect.zero.2
1426  testing:
1427  - 2d.path.rect.subpath
1428  code: |
1429    ctx.fillStyle = '#f00';
1430    ctx.fillRect(0, 0, 100, 50);
1431    ctx.strokeStyle = '#0f0';
1432    ctx.lineWidth = 100;
1433    ctx.beginPath();
1434    ctx.rect(50, -100, 0, 250);
1435    ctx.stroke();
1436    @assert pixel 50,25 == 0,255,0,255;
1437  expected: green
1438
1439- name: 2d.path.rect.zero.3
1440  testing:
1441  - 2d.path.rect.subpath
1442  code: |
1443    ctx.fillStyle = '#0f0';
1444    ctx.fillRect(0, 0, 100, 50);
1445    ctx.strokeStyle = '#f00';
1446    ctx.lineWidth = 100;
1447    ctx.beginPath();
1448    ctx.rect(50, 25, 0, 0);
1449    ctx.stroke();
1450    @assert pixel 50,25 == 0,255,0,255;
1451  expected: green
1452
1453- name: 2d.path.rect.zero.4
1454  testing:
1455  - 2d.path.rect.subpath
1456  code: |
1457    ctx.fillStyle = '#f00';
1458    ctx.fillRect(0, 0, 100, 50);
1459    ctx.strokeStyle = '#0f0';
1460    ctx.lineWidth = 50;
1461    ctx.rect(100, 25, 0, 0);
1462    ctx.lineTo(0, 25);
1463    ctx.stroke();
1464    @assert pixel 50,25 == 0,255,0,255;
1465  expected: green
1466
1467- name: 2d.path.rect.zero.5
1468  testing:
1469  - 2d.path.rect.subpath
1470  code: |
1471    ctx.fillStyle = '#0f0';
1472    ctx.fillRect(0, 0, 100, 50);
1473    ctx.strokeStyle = '#f00';
1474    ctx.lineWidth = 50;
1475    ctx.moveTo(0, 0);
1476    ctx.rect(100, 25, 0, 0);
1477    ctx.stroke();
1478    @assert pixel 50,25 == 0,255,0,255;
1479  expected: green
1480
1481- name: 2d.path.rect.zero.6
1482  testing:
1483  - 2d.path.rect.subpath
1484  #mozilla: { bug: TODO }
1485  code: |
1486    ctx.fillStyle = '#0f0';
1487    ctx.fillRect(0, 0, 100, 50);
1488    ctx.strokeStyle = '#f00';
1489    ctx.lineJoin = 'miter';
1490    ctx.miterLimit = 1.5;
1491    ctx.lineWidth = 200;
1492    ctx.beginPath();
1493    ctx.rect(100, 25, 1000, 0);
1494    ctx.stroke();
1495    @assert pixel 50,25 == 0,255,0,255; @moz-todo
1496  expected: green
1497
1498- name: 2d.path.rect.negative
1499  testing:
1500  - 2d.path.rect.subpath
1501  code: |
1502    ctx.fillStyle = '#f00';
1503    ctx.fillRect(0, 0, 100, 50);
1504    ctx.beginPath();
1505    ctx.fillStyle = '#0f0';
1506    ctx.rect(0, 0, 50, 25);
1507    ctx.rect(100, 0, -50, 25);
1508    ctx.rect(0, 50, 50, -25);
1509    ctx.rect(100, 50, -50, -25);
1510    ctx.fill();
1511    @assert pixel 25,12 == 0,255,0,255;
1512    @assert pixel 75,12 == 0,255,0,255;
1513    @assert pixel 25,37 == 0,255,0,255;
1514    @assert pixel 75,37 == 0,255,0,255;
1515
1516- name: 2d.path.rect.winding
1517  testing:
1518  - 2d.path.rect.subpath
1519  code: |
1520    ctx.fillStyle = '#0f0';
1521    ctx.fillRect(0, 0, 100, 50);
1522    ctx.beginPath();
1523    ctx.fillStyle = '#f00';
1524    ctx.rect(0, 0, 50, 50);
1525    ctx.rect(100, 50, -50, -50);
1526    ctx.rect(0, 25, 100, -25);
1527    ctx.rect(100, 25, -100, 25);
1528    ctx.fill();
1529    @assert pixel 25,12 == 0,255,0,255;
1530    @assert pixel 75,12 == 0,255,0,255;
1531    @assert pixel 25,37 == 0,255,0,255;
1532    @assert pixel 75,37 == 0,255,0,255;
1533
1534- name: 2d.path.rect.selfintersect
1535  #mozilla: { bug: TODO }
1536  code: |
1537    ctx.fillStyle = '#f00';
1538    ctx.fillRect(0, 0, 100, 50);
1539    ctx.strokeStyle = '#0f0';
1540    ctx.lineWidth = 90;
1541    ctx.beginPath();
1542    ctx.rect(45, 20, 10, 10);
1543    ctx.stroke();
1544    @assert pixel 50,25 == 0,255,0,255; @moz-todo
1545  expected: green
1546
1547- name: 2d.path.rect.nonfinite
1548  desc: rect() with Infinity/NaN is ignored
1549  testing:
1550  - 2d.nonfinite
1551  code: |
1552    ctx.moveTo(0, 0);
1553    ctx.lineTo(100, 0);
1554    @nonfinite ctx.rect(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>);
1555    ctx.lineTo(100, 50);
1556    ctx.lineTo(0, 50);
1557    ctx.fillStyle = '#0f0';
1558    ctx.fill();
1559    @assert pixel 50,25 == 0,255,0,255;
1560    @assert pixel 90,45 == 0,255,0,255;
1561  expected: green
1562
1563- name: 2d.path.roundrect.newsubpath
1564  code: |
1565    ctx.fillStyle = '#0f0';
1566    ctx.fillRect(0, 0, 100, 50);
1567    ctx.beginPath();
1568    ctx.strokeStyle = '#f00';
1569    ctx.lineWidth = 50;
1570    ctx.moveTo(-100, 25);
1571    ctx.lineTo(-50, 25);
1572    ctx.roundRect(200, 25, 1, 1, [0]);
1573    ctx.stroke();
1574    @assert pixel 50,25 == 0,255,0,255;
1575  expected: green
1576
1577- name: 2d.path.roundrect.closed
1578  code: |
1579    ctx.fillStyle = '#f00';
1580    ctx.fillRect(0, 0, 100, 50);
1581    ctx.strokeStyle = '#0f0';
1582    ctx.lineWidth = 200;
1583    ctx.lineJoin = 'miter';
1584    ctx.roundRect(100, 50, 100, 100, [0]);
1585    ctx.stroke();
1586    @assert pixel 50,25 == 0,255,0,255;
1587  expected: green
1588
1589- name: 2d.path.roundrect.end.1
1590  code: |
1591    ctx.fillStyle = '#f00';
1592    ctx.fillRect(0, 0, 100, 50);
1593    ctx.strokeStyle = '#0f0';
1594    ctx.lineWidth = 100;
1595    ctx.roundRect(200, 100, 400, 1000, [0]);
1596    ctx.lineTo(-2000, -1000);
1597    ctx.stroke();
1598    @assert pixel 50,25 == 0,255,0,255;
1599  expected: green
1600
1601- name: 2d.path.roundrect.end.2
1602  code: |
1603    ctx.fillStyle = '#f00';
1604    ctx.fillRect(0, 0, 100, 50);
1605    ctx.strokeStyle = '#0f0';
1606    ctx.lineWidth = 450;
1607    ctx.lineCap = 'round';
1608    ctx.lineJoin = 'bevel';
1609    ctx.roundRect(150, 150, 2000, 2000, [0]);
1610    ctx.lineTo(160, 160);
1611    ctx.stroke();
1612    @assert pixel 1,1 == 0,255,0,255;
1613    @assert pixel 98,1 == 0,255,0,255;
1614    @assert pixel 1,48 == 0,255,0,255;
1615    @assert pixel 98,48 == 0,255,0,255;
1616  expected: green
1617
1618- name: 2d.path.roundrect.end.3
1619  code: |
1620    ctx.fillStyle = '#f00';
1621    ctx.fillRect(0, 0, 100, 50);
1622    ctx.strokeStyle = '#0f0';
1623    ctx.lineWidth = 100;
1624    ctx.roundRect(101, 51, 2000, 2000, [500, 500, 500, 500]);
1625    ctx.lineTo(-1, -1);
1626    ctx.stroke();
1627    @assert pixel 1,1 == 0,255,0,255;
1628    @assert pixel 98,1 == 0,255,0,255;
1629    @assert pixel 1,48 == 0,255,0,255;
1630    @assert pixel 98,48 == 0,255,0,255;
1631  expected: green
1632
1633- name: 2d.path.roundrect.end.4
1634  code: |
1635    ctx.fillStyle = '#0f0';
1636    ctx.fillRect(0, 0, 100, 50);
1637    ctx.strokeStyle = '#f00';
1638    ctx.lineWidth = 10;
1639    ctx.roundRect(-1, -1, 2000, 2000, [1000, 1000, 1000, 1000]);
1640    ctx.lineTo(-150, -150);
1641    ctx.stroke();
1642    @assert pixel 1,1 == 0,255,0,255;
1643    @assert pixel 98,1 == 0,255,0,255;
1644    @assert pixel 1,48 == 0,255,0,255;
1645    @assert pixel 98,48 == 0,255,0,255;
1646  expected: green
1647
1648- name: 2d.path.roundrect.zero.1
1649  code: |
1650    ctx.fillStyle = '#f00';
1651    ctx.fillRect(0, 0, 100, 50);
1652    ctx.strokeStyle = '#0f0';
1653    ctx.lineWidth = 100;
1654    ctx.beginPath();
1655    ctx.roundRect(0, 50, 100, 0, [0]);
1656    ctx.stroke();
1657    @assert pixel 50,25 == 0,255,0,255;
1658  expected: green
1659
1660- name: 2d.path.roundrect.zero.2
1661  code: |
1662    ctx.fillStyle = '#f00';
1663    ctx.fillRect(0, 0, 100, 50);
1664    ctx.strokeStyle = '#0f0';
1665    ctx.lineWidth = 100;
1666    ctx.beginPath();
1667    ctx.roundRect(50, -100, 0, 250, [0]);
1668    ctx.stroke();
1669    @assert pixel 50,25 == 0,255,0,255;
1670  expected: green
1671
1672- name: 2d.path.roundrect.zero.3
1673  code: |
1674    ctx.fillStyle = '#0f0';
1675    ctx.fillRect(0, 0, 100, 50);
1676    ctx.strokeStyle = '#f00';
1677    ctx.lineWidth = 100;
1678    ctx.beginPath();
1679    ctx.roundRect(50, 25, 0, 0, [0]);
1680    ctx.stroke();
1681    @assert pixel 50,25 == 0,255,0,255;
1682  expected: green
1683
1684- name: 2d.path.roundrect.zero.4
1685  code: |
1686    ctx.fillStyle = '#f00';
1687    ctx.fillRect(0, 0, 100, 50);
1688    ctx.strokeStyle = '#0f0';
1689    ctx.lineWidth = 50;
1690    ctx.roundRect(100, 25, 0, 0, [0]);
1691    ctx.lineTo(0, 25);
1692    ctx.stroke();
1693    @assert pixel 50,25 == 0,255,0,255;
1694  expected: green
1695
1696- name: 2d.path.roundrect.zero.5
1697  code: |
1698    ctx.fillStyle = '#0f0';
1699    ctx.fillRect(0, 0, 100, 50);
1700    ctx.strokeStyle = '#f00';
1701    ctx.lineWidth = 50;
1702    ctx.moveTo(0, 0);
1703    ctx.roundRect(100, 25, 0, 0, [0]);
1704    ctx.stroke();
1705    @assert pixel 50,25 == 0,255,0,255;
1706  expected: green
1707
1708- name: 2d.path.roundrect.zero.6
1709  code: |
1710    ctx.fillStyle = '#0f0';
1711    ctx.fillRect(0, 0, 100, 50);
1712    ctx.strokeStyle = '#f00';
1713    ctx.lineJoin = 'miter';
1714    ctx.miterLimit = 1.5;
1715    ctx.lineWidth = 200;
1716    ctx.beginPath();
1717    ctx.roundRect(100, 25, 1000, 0, [0]);
1718    ctx.stroke();
1719    @assert pixel 50,25 == 0,255,0,255;
1720  expected: green
1721
1722- name: 2d.path.roundrect.negative
1723  code: |
1724    ctx.fillStyle = '#f00';
1725    ctx.fillRect(0, 0, 100, 50);
1726    ctx.beginPath();
1727    ctx.fillStyle = '#0f0';
1728    ctx.roundRect(0, 0, 50, 25, [10, 0, 0, 0]);
1729    ctx.roundRect(100, 0, -50, 25, [10, 0, 0, 0]);
1730    ctx.roundRect(0, 50, 50, -25, [10, 0, 0, 0]);
1731    ctx.roundRect(100, 50, -50, -25, [10, 0, 0, 0]);
1732    ctx.fill();
1733    // All rects drawn
1734    @assert pixel 25,12 == 0,255,0,255;
1735    @assert pixel 75,12 == 0,255,0,255;
1736    @assert pixel 25,37 == 0,255,0,255;
1737    @assert pixel 75,37 == 0,255,0,255;
1738    // Correct corners are rounded.
1739    @assert pixel 1,1 == 255,0,0,255;
1740    @assert pixel 98,1 == 255,0,0,255;
1741    @assert pixel 1,48 == 255,0,0,255;
1742    @assert pixel 98,48 == 255,0,0,255;
1743
1744- name: 2d.path.roundrect.winding
1745  code: |
1746    ctx.fillStyle = '#0f0';
1747    ctx.fillRect(0, 0, 100, 50);
1748    ctx.beginPath();
1749    ctx.fillStyle = '#f00';
1750    ctx.roundRect(0, 0, 50, 50, [0]);
1751    ctx.roundRect(100, 50, -50, -50, [0]);
1752    ctx.roundRect(0, 25, 100, -25, [0]);
1753    ctx.roundRect(100, 25, -100, 25, [0]);
1754    ctx.fill();
1755    @assert pixel 25,12 == 0,255,0,255;
1756    @assert pixel 75,12 == 0,255,0,255;
1757    @assert pixel 25,37 == 0,255,0,255;
1758    @assert pixel 75,37 == 0,255,0,255;
1759
1760- name: 2d.path.roundrect.selfintersect
1761  code: |
1762    ctx.fillStyle = '#f00';
1763    ctx.roundRect(0, 0, 100, 50, [0]);
1764    ctx.strokeStyle = '#0f0';
1765    ctx.lineWidth = 90;
1766    ctx.beginPath();
1767    ctx.roundRect(45, 20, 10, 10, [0]);
1768    ctx.stroke();
1769    @assert pixel 50,25 == 0,255,0,255;
1770  expected: green
1771
1772- name: 2d.path.roundrect.nonfinite
1773  desc: roundRect() with Infinity/NaN is ignored
1774  testing:
1775  - 2d.nonfinite
1776  code: |
1777    ctx.fillStyle = '#f00';
1778    ctx.fillRect(0, 0, 100, 50)
1779    ctx.moveTo(0, 0);
1780    ctx.lineTo(100, 0);
1781    @nonfinite ctx.roundRect(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <[0] [Infinity] [-Infinity] [NaN] [Infinity,0] [-Infinity,0] [NaN,0] [0,Infinity] [0,-Infinity] [0,NaN] [Infinity,0,0] [-Infinity,0,0] [NaN,0,0] [0,Infinity,0] [0,-Infinity,0] [0,NaN,0] [0,0,Infinity] [0,0,-Infinity] [0,0,NaN] [Infinity,0,0,0] [-Infinity,0,0,0] [NaN,0,0,0] [0,Infinity,0,0] [0,-Infinity,0,0] [0,NaN,0,0] [0,0,Infinity,0] [0,0,-Infinity,0] [0,0,NaN,0] [0,0,0,Infinity] [0,0,0,-Infinity] [0,0,0,NaN]>);
1782    ctx.lineTo(100, 50);
1783    ctx.lineTo(0, 50);
1784    ctx.fillStyle = '#0f0';
1785    ctx.fill();
1786    @assert pixel 50,25 == 0,255,0,255;
1787    @assert pixel 90,45 == 0,255,0,255;
1788  expected: green
1789
1790- name: 2d.path.roundrect.4.radii.1.double
1791  desc: Verify that when four radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.
1792  code: |
1793    ctx.fillStyle = '#f00';
1794    ctx.fillRect(0, 0, 100, 50);
1795    ctx.roundRect(0, 0, 100, 50, [20, 0, 0, 0]);
1796    ctx.fillStyle = '#0f0';
1797    ctx.fill();
1798    @assert pixel 1,1 == 255,0,0,255;
1799    @assert pixel 98,1 == 0,255,0,255;
1800    @assert pixel 98,48 == 0,255,0,255;
1801    @assert pixel 1,48 == 0,255,0,255;
1802
1803- name: 2d.path.roundrect.4.radii.1.dompoint
1804  desc: Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.
1805  code: |
1806    ctx.fillStyle = '#f00';
1807    ctx.fillRect(0, 0, 100, 50);
1808    ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0, 0]);
1809    ctx.fillStyle = '#0f0';
1810    ctx.fill();
1811
1812    // top-left corner
1813    @assert pixel 20,1 == 255,0,0,255;
1814    @assert pixel 41,1 == 0,255,0,255;
1815    @assert pixel 1,10 == 255,0,0,255;
1816    @assert pixel 1,21 == 0,255,0,255;
1817
1818    // other corners
1819    @assert pixel 98,1 == 0,255,0,255;
1820    @assert pixel 98,48 == 0,255,0,255;
1821    @assert pixel 1,48 == 0,255,0,255;
1822
1823- name: 2d.path.roundrect.4.radii.2.double
1824  desc: Verify that when four radii are given to roundRect(), the second radius, specified as a double, applies to the top-right corner.
1825  code: |
1826    ctx.fillStyle = '#f00';
1827    ctx.fillRect(0, 0, 100, 50);
1828    ctx.roundRect(0, 0, 100, 50, [0, 20, 0, 0]);
1829    ctx.fillStyle = '#0f0';
1830    ctx.fill();
1831    @assert pixel 1,1 == 0,255,0,255;
1832    @assert pixel 98,1 == 255,0,0,255;
1833    @assert pixel 98,48 == 0,255,0,255;
1834    @assert pixel 1,48 == 0,255,0,255;
1835
1836- name: 2d.path.roundrect.4.radii.2.dompoint
1837  desc: Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right corner.
1838  code: |
1839    ctx.fillStyle = '#f00';
1840    ctx.fillRect(0, 0, 100, 50);
1841    ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0, 0]);
1842    ctx.fillStyle = '#0f0';
1843    ctx.fill();
1844
1845    // top-right corner
1846    @assert pixel 79,1 == 255,0,0,255;
1847    @assert pixel 58,1 == 0,255,0,255;
1848    @assert pixel 98,10 == 255,0,0,255;
1849    @assert pixel 98,21 == 0,255,0,255;
1850
1851    // other corners
1852    @assert pixel 1,1 == 0,255,0,255;
1853    @assert pixel 98,48 == 0,255,0,255;
1854    @assert pixel 1,48 == 0,255,0,255;
1855
1856- name: 2d.path.roundrect.4.radii.3.double
1857  desc: Verify that when four radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.
1858  code: |
1859    ctx.fillStyle = '#f00';
1860    ctx.fillRect(0, 0, 100, 50);
1861    ctx.roundRect(0, 0, 100, 50, [0, 0, 20, 0]);
1862    ctx.fillStyle = '#0f0';
1863    ctx.fill();
1864    @assert pixel 1,1 == 0,255,0,255;
1865    @assert pixel 98,1 == 0,255,0,255;
1866    @assert pixel 98,48 == 255,0,0,255;
1867    @assert pixel 1,48 == 0,255,0,255;
1868
1869- name: 2d.path.roundrect.4.radii.3.dompoint
1870  desc: Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.
1871  code: |
1872    ctx.fillStyle = '#f00';
1873    ctx.fillRect(0, 0, 100, 50);
1874    ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20), 0]);
1875    ctx.fillStyle = '#0f0';
1876    ctx.fill();
1877
1878    // bottom-right corner
1879    @assert pixel 79,48 == 255,0,0,255;
1880    @assert pixel 58,48 == 0,255,0,255;
1881    @assert pixel 98,39 == 255,0,0,255;
1882    @assert pixel 98,28 == 0,255,0,255;
1883
1884    // other corners
1885    @assert pixel 1,1 == 0,255,0,255;
1886    @assert pixel 98,1 == 0,255,0,255;
1887    @assert pixel 1,48 == 0,255,0,255;
1888
1889- name: 2d.path.roundrect.4.radii.4.double
1890  desc: Verify that when four radii are given to roundRect(), the fourth radius, specified as a double, applies to the bottom-left corner.
1891  code: |
1892    ctx.fillStyle = '#f00';
1893    ctx.fillRect(0, 0, 100, 50);
1894    ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 20]);
1895    ctx.fillStyle = '#0f0';
1896    ctx.fill();
1897    @assert pixel 1,1 == 0,255,0,255;
1898    @assert pixel 98,1 == 0,255,0,255;
1899    @assert pixel 98,48 == 0,255,0,255;
1900    @assert pixel 1,48 == 255,0,0,255;
1901
1902- name: 2d.path.roundrect.4.radii.4.dompoint
1903  desc: Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPoint, applies to the bottom-left corner.
1904  code: |
1905    ctx.fillStyle = '#f00';
1906    ctx.fillRect(0, 0, 100, 50);
1907    ctx.roundRect(0, 0, 100, 50, [0, 0, 0, new DOMPoint(40, 20)]);
1908    ctx.fillStyle = '#0f0';
1909    ctx.fill();
1910
1911    // bottom-left corner
1912    @assert pixel 20,48 == 255,0,0,255;
1913    @assert pixel 41,48 == 0,255,0,255;
1914    @assert pixel 1,39 == 255,0,0,255;
1915    @assert pixel 1,28 == 0,255,0,255;
1916
1917    // other corners
1918    @assert pixel 1,1 == 0,255,0,255;
1919    @assert pixel 98,1 == 0,255,0,255;
1920    @assert pixel 98,48 == 0,255,0,255;
1921
1922- name: 2d.path.roundrect.3.radii.1.double
1923  desc: Verify that when three radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.
1924  code: |
1925    ctx.fillStyle = '#f00';
1926    ctx.fillRect(0, 0, 100, 50);
1927    ctx.roundRect(0, 0, 100, 50, [20, 0, 0]);
1928    ctx.fillStyle = '#0f0';
1929    ctx.fill();
1930    @assert pixel 1,1 == 255,0,0,255;
1931    @assert pixel 98,1 == 0,255,0,255;
1932    @assert pixel 98,48 == 0,255,0,255;
1933    @assert pixel 1,48 == 0,255,0,255;
1934
1935- name: 2d.path.roundrect.3.radii.1.dompoint
1936  desc: Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.
1937  code: |
1938    ctx.fillStyle = '#f00';
1939    ctx.fillRect(0, 0, 100, 50);
1940    ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0]);
1941    ctx.fillStyle = '#0f0';
1942    ctx.fill();
1943
1944    // top-left corner
1945    @assert pixel 20,1 == 255,0,0,255;
1946    @assert pixel 41,1 == 0,255,0,255;
1947    @assert pixel 1,10 == 255,0,0,255;
1948    @assert pixel 1,21 == 0,255,0,255;
1949
1950    // other corners
1951    @assert pixel 98,1 == 0,255,0,255;
1952    @assert pixel 98,48 == 0,255,0,255;
1953    @assert pixel 1,48 == 0,255,0,255;
1954
1955- name: 2d.path.roundrect.3.radii.2.double
1956  desc: Verify that when three radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.
1957  code: |
1958    ctx.fillStyle = '#f00';
1959    ctx.fillRect(0, 0, 100, 50);
1960    ctx.roundRect(0, 0, 100, 50, [0, 20, 0]);
1961    ctx.fillStyle = '#0f0';
1962    ctx.fill();
1963    @assert pixel 1,1 == 0,255,0,255;
1964    @assert pixel 98,1 == 255,0,0,255;
1965    @assert pixel 98,48 == 0,255,0,255;
1966    @assert pixel 1,48 == 255,0,0,255;
1967
1968- name: 2d.path.roundrect.3.radii.2.dompoint
1969  desc: Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.
1970  code: |
1971    ctx.fillStyle = '#f00';
1972    ctx.fillRect(0, 0, 100, 50);
1973    ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0]);
1974    ctx.fillStyle = '#0f0';
1975    ctx.fill();
1976
1977    // top-right corner
1978    @assert pixel 79,1 == 255,0,0,255;
1979    @assert pixel 58,1 == 0,255,0,255;
1980    @assert pixel 98,10 == 255,0,0,255;
1981    @assert pixel 98,21 == 0,255,0,255;
1982
1983    // bottom-left corner
1984    @assert pixel 20,48 == 255,0,0,255;
1985    @assert pixel 41,48 == 0,255,0,255;
1986    @assert pixel 1,39 == 255,0,0,255;
1987    @assert pixel 1,28 == 0,255,0,255;
1988
1989    // other corners
1990    @assert pixel 1,1 == 0,255,0,255;
1991    @assert pixel 98,48 == 0,255,0,255;
1992
1993- name: 2d.path.roundrect.3.radii.3.double
1994  desc: Verify that when three radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.
1995  code: |
1996    ctx.fillStyle = '#f00';
1997    ctx.fillRect(0, 0, 100, 50);
1998    ctx.roundRect(0, 0, 100, 50, [0, 0, 20]);
1999    ctx.fillStyle = '#0f0';
2000    ctx.fill();
2001    @assert pixel 1,1 == 0,255,0,255;
2002    @assert pixel 98,1 == 0,255,0,255;
2003    @assert pixel 98,48 == 255,0,0,255;
2004    @assert pixel 1,48 == 0,255,0,255;
2005
2006- name: 2d.path.roundrect.3.radii.3.dompoint
2007  desc: Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.
2008  code: |
2009    ctx.fillStyle = '#f00';
2010    ctx.fillRect(0, 0, 100, 50);
2011    ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20)]);
2012    ctx.fillStyle = '#0f0';
2013    ctx.fill();
2014
2015    // bottom-right corner
2016    @assert pixel 79,48 == 255,0,0,255;
2017    @assert pixel 58,48 == 0,255,0,255;
2018    @assert pixel 98,39 == 255,0,0,255;
2019    @assert pixel 98,28 == 0,255,0,255;
2020
2021    // other corners
2022    @assert pixel 1,1 == 0,255,0,255;
2023    @assert pixel 98,1 == 0,255,0,255;
2024    @assert pixel 1,48 == 0,255,0,255;
2025
2026- name: 2d.path.roundrect.2.radii.1.double
2027  desc: Verify that when two radii are given to roundRect(), the first radius, specified as a double, applies to the top-left and bottom-right corners.
2028  code: |
2029    ctx.fillStyle = '#f00';
2030    ctx.fillRect(0, 0, 100, 50);
2031    ctx.roundRect(0, 0, 100, 50, [20, 0]);
2032    ctx.fillStyle = '#0f0';
2033    ctx.fill();
2034    @assert pixel 1,1 == 255,0,0,255;
2035    @assert pixel 98,1 == 0,255,0,255;
2036    @assert pixel 98,48 == 255,0,0,255;
2037    @assert pixel 1,48 == 0,255,0,255;
2038
2039- name: 2d.path.roundrect.2.radii.1.dompoint
2040  desc: Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-right corners.
2041  code: |
2042    ctx.fillStyle = '#f00';
2043    ctx.fillRect(0, 0, 100, 50);
2044    ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0]);
2045    ctx.fillStyle = '#0f0';
2046    ctx.fill();
2047
2048    // top-left corner
2049    @assert pixel 20,1 == 255,0,0,255;
2050    @assert pixel 41,1 == 0,255,0,255;
2051    @assert pixel 1,10 == 255,0,0,255;
2052    @assert pixel 1,21 == 0,255,0,255;
2053
2054    // bottom-right corner
2055    @assert pixel 79,48 == 255,0,0,255;
2056    @assert pixel 58,48 == 0,255,0,255;
2057    @assert pixel 98,39 == 255,0,0,255;
2058    @assert pixel 98,28 == 0,255,0,255;
2059
2060    // other corners
2061    @assert pixel 98,1 == 0,255,0,255;
2062    @assert pixel 1,48 == 0,255,0,255;
2063
2064- name: 2d.path.roundrect.2.radii.2.double
2065  desc: Verify that when two radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.
2066  code: |
2067    ctx.fillStyle = '#f00';
2068    ctx.fillRect(0, 0, 100, 50);
2069    ctx.roundRect(0, 0, 100, 50, [0, 20]);
2070    ctx.fillStyle = '#0f0';
2071    ctx.fill();
2072    @assert pixel 1,1 == 0,255,0,255;
2073    @assert pixel 98,1 == 255,0,0,255;
2074    @assert pixel 98,48 == 0,255,0,255;
2075    @assert pixel 1,48 == 255,0,0,255;
2076
2077- name: 2d.path.roundrect.2.radii.2.dompoint
2078  desc: Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.
2079  code: |
2080    ctx.fillStyle = '#f00';
2081    ctx.fillRect(0, 0, 100, 50);
2082    ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20)]);
2083    ctx.fillStyle = '#0f0';
2084    ctx.fill();
2085
2086    // top-right corner
2087    @assert pixel 79,1 == 255,0,0,255;
2088    @assert pixel 58,1 == 0,255,0,255;
2089    @assert pixel 98,10 == 255,0,0,255;
2090    @assert pixel 98,21 == 0,255,0,255;
2091
2092    // bottom-left corner
2093    @assert pixel 20,48 == 255,0,0,255;
2094    @assert pixel 41,48 == 0,255,0,255;
2095    @assert pixel 1,39 == 255,0,0,255;
2096    @assert pixel 1,28 == 0,255,0,255;
2097
2098    // other corners
2099    @assert pixel 1,1 == 0,255,0,255;
2100    @assert pixel 98,48 == 0,255,0,255;
2101
2102- name: 2d.path.roundrect.1.radius.double
2103  desc: Verify that when one radius is given to roundRect(), specified as a double, it applies to all corners.
2104  code: |
2105    ctx.fillStyle = '#f00';
2106    ctx.fillRect(0, 0, 100, 50);
2107    ctx.roundRect(0, 0, 100, 50, [20]);
2108    ctx.fillStyle = '#0f0';
2109    ctx.fill();
2110    @assert pixel 1,1 == 255,0,0,255;
2111    @assert pixel 98,1 == 255,0,0,255;
2112    @assert pixel 98,48 == 255,0,0,255;
2113    @assert pixel 1,48 == 255,0,0,255;
2114
2115- name: 2d.path.roundrect.1.radius.dompoint
2116  desc: Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-left corners.
2117  code: |
2118    ctx.fillStyle = '#f00';
2119    ctx.fillRect(0, 0, 100, 50);
2120    ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20)]);
2121    ctx.fillStyle = '#0f0';
2122    ctx.fill();
2123
2124    // top-left corner
2125    @assert pixel 20,1 == 255,0,0,255;
2126    @assert pixel 41,1 == 0,255,0,255;
2127    @assert pixel 1,10 == 255,0,0,255;
2128    @assert pixel 1,21 == 0,255,0,255;
2129
2130    // top-right corner
2131    @assert pixel 79,1 == 255,0,0,255;
2132    @assert pixel 58,1 == 0,255,0,255;
2133    @assert pixel 98,10 == 255,0,0,255;
2134    @assert pixel 98,21 == 0,255,0,255;
2135
2136    // bottom-right corner
2137    @assert pixel 79,48 == 255,0,0,255;
2138    @assert pixel 58,48 == 0,255,0,255;
2139    @assert pixel 98,39 == 255,0,0,255;
2140    @assert pixel 98,28 == 0,255,0,255;
2141
2142    // bottom-left corner
2143    @assert pixel 20,48 == 255,0,0,255;
2144    @assert pixel 41,48 == 0,255,0,255;
2145    @assert pixel 1,39 == 255,0,0,255;
2146    @assert pixel 1,28 == 0,255,0,255;
2147
2148- name: 2d.path.roundrect.radius.intersecting.1
2149  desc: Check that roundRects with intersecting corner arcs are rendered correctly.
2150  code: |
2151    ctx.fillStyle = '#f00';
2152    ctx.fillRect(0, 0, 100, 50);
2153    ctx.roundRect(0, 0, 100, 50, [40, 40, 40, 40]);
2154    ctx.fillStyle = '#0f0';
2155    ctx.fill();
2156    @assert pixel 2,25 == 0,255,0,255;
2157    @assert pixel 50,1 == 0,255,0,255;
2158    @assert pixel 50,25 == 0,255,0,255;
2159    @assert pixel 50,48 == 0,255,0,255;
2160    @assert pixel 97,25 == 0,255,0,255;
2161    @assert pixel 1,1 == 255,0,0,255;
2162    @assert pixel 98,1 == 255,0,0,255;
2163    @assert pixel 1,48 == 255,0,0,255;
2164    @assert pixel 98,48 == 255,0,0,255;
2165
2166- name: 2d.path.roundrect.radius.intersecting.2
2167  desc: Check that roundRects with intersecting corner arcs are rendered correctly.
2168  code: |
2169    ctx.fillStyle = '#f00';
2170    ctx.fillRect(0, 0, 100, 50);
2171    ctx.roundRect(0, 0, 100, 50, [1000, 1000, 1000, 1000]);
2172    ctx.fillStyle = '#0f0';
2173    ctx.fill();
2174    @assert pixel 2,25 == 0,255,0,255;
2175    @assert pixel 50,1 == 0,255,0,255;
2176    @assert pixel 50,25 == 0,255,0,255;
2177    @assert pixel 50,48 == 0,255,0,255;
2178    @assert pixel 97,25 == 0,255,0,255;
2179    @assert pixel 1,1 == 255,0,0,255;
2180    @assert pixel 98,1 == 255,0,0,255;
2181    @assert pixel 1,48 == 255,0,0,255;
2182    @assert pixel 98,48 == 255,0,0,255;
2183
2184- name: 2d.path.roundrect.radius.none
2185  desc: Check that roundRect throws an IndexSizeError if radii is an empty array.
2186  code: |
2187    @assert throws INDEX_SIZE_ERR ctx.roundRect(0, 0, 100, 50, []);
2188
2189- name: 2d.path.roundrect.radius.toomany
2190  desc: Check that roundRect throws an IndeSizeError if radii has more than four items.
2191  code: |
2192    @assert throws INDEX_SIZE_ERR ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 0, 0]);
2193
2194- name: 2d.path.ellipse.basics
2195  desc: Verify canvas throws error when drawing ellipse with negative radii.
2196  testing:
2197  - 2d.ellipse.basics
2198  code: |
2199    ctx.ellipse(10, 10, 10, 5, 0, 0, 1, false);
2200    ctx.ellipse(10, 10, 10, 0, 0, 0, 1, false);
2201    ctx.ellipse(10, 10, -0, 5, 0, 0, 1, false);
2202    @assert throws INDEX_SIZE_ERR ctx.ellipse(10, 10, -2, 5, 0, 0, 1, false);
2203    @assert throws INDEX_SIZE_ERR ctx.ellipse(10, 10, 0, -1.5, 0, 0, 1, false);
2204    @assert throws INDEX_SIZE_ERR ctx.ellipse(10, 10, -2, -5, 0, 0, 1, false);
2205    ctx.ellipse(80, 0, 10, 4294967277, Math.PI / -84, -Math.PI / 2147483436, false);
2206
2207- name: 2d.path.fill.overlap
2208  testing:
2209  - 2d.path.fill.basic
2210  code: |
2211    ctx.fillStyle = '#000';
2212    ctx.fillRect(0, 0, 100, 50);
2213
2214    ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
2215    ctx.rect(0, 0, 100, 50);
2216    ctx.closePath();
2217    ctx.rect(10, 10, 80, 30);
2218    ctx.fill();
2219
2220    @assert pixel 50,25 ==~ 0,127,0,255 +/- 1;
2221  expected: |
2222    size 100 50
2223    cr.set_source_rgb(0, 0.5, 0)
2224    cr.rectangle(0, 0, 100, 50)
2225    cr.fill()
2226
2227- name: 2d.path.fill.winding.add
2228  testing:
2229  - 2d.path.fill.basic
2230  code: |
2231    ctx.fillStyle = '#f00';
2232    ctx.fillRect(0, 0, 100, 50);
2233
2234    ctx.fillStyle = '#0f0';
2235    ctx.moveTo(-10, -10);
2236    ctx.lineTo(110, -10);
2237    ctx.lineTo(110, 60);
2238    ctx.lineTo(-10, 60);
2239    ctx.lineTo(-10, -10);
2240    ctx.lineTo(0, 0);
2241    ctx.lineTo(100, 0);
2242    ctx.lineTo(100, 50);
2243    ctx.lineTo(0, 50);
2244    ctx.fill();
2245
2246    @assert pixel 50,25 == 0,255,0,255;
2247  expected: green
2248
2249- name: 2d.path.fill.winding.subtract.1
2250  testing:
2251  - 2d.path.fill.basic
2252  code: |
2253    ctx.fillStyle = '#0f0';
2254    ctx.fillRect(0, 0, 100, 50);
2255
2256    ctx.fillStyle = '#f00';
2257    ctx.moveTo(-10, -10);
2258    ctx.lineTo(110, -10);
2259    ctx.lineTo(110, 60);
2260    ctx.lineTo(-10, 60);
2261    ctx.lineTo(-10, -10);
2262    ctx.lineTo(0, 0);
2263    ctx.lineTo(0, 50);
2264    ctx.lineTo(100, 50);
2265    ctx.lineTo(100, 0);
2266    ctx.fill();
2267
2268    @assert pixel 50,25 == 0,255,0,255;
2269  expected: green
2270
2271- name: 2d.path.fill.winding.subtract.2
2272  testing:
2273  - 2d.path.fill.basic
2274  code: |
2275    ctx.fillStyle = '#0f0';
2276    ctx.fillRect(0, 0, 100, 50);
2277
2278    ctx.fillStyle = '#f00';
2279    ctx.moveTo(-10, -10);
2280    ctx.lineTo(110, -10);
2281    ctx.lineTo(110, 60);
2282    ctx.lineTo(-10, 60);
2283    ctx.moveTo(0, 0);
2284    ctx.lineTo(0, 50);
2285    ctx.lineTo(100, 50);
2286    ctx.lineTo(100, 0);
2287    ctx.fill();
2288
2289    @assert pixel 50,25 == 0,255,0,255;
2290  expected: green
2291
2292- name: 2d.path.fill.winding.subtract.3
2293  testing:
2294  - 2d.path.fill.basic
2295  code: |
2296    ctx.fillStyle = '#f00';
2297    ctx.fillRect(0, 0, 100, 50);
2298
2299    ctx.fillStyle = '#0f0';
2300    ctx.moveTo(-10, -10);
2301    ctx.lineTo(110, -10);
2302    ctx.lineTo(110, 60);
2303    ctx.lineTo(-10, 60);
2304    ctx.lineTo(-10, -10);
2305    ctx.lineTo(-20, -20);
2306    ctx.lineTo(120, -20);
2307    ctx.lineTo(120, 70);
2308    ctx.lineTo(-20, 70);
2309    ctx.lineTo(-20, -20);
2310    ctx.lineTo(0, 0);
2311    ctx.lineTo(0, 50);
2312    ctx.lineTo(100, 50);
2313    ctx.lineTo(100, 0);
2314    ctx.fill();
2315
2316    @assert pixel 50,25 == 0,255,0,255;
2317  expected: green
2318
2319- name: 2d.path.fill.closed.basic
2320  testing:
2321  - 2d.path.fill.closed
2322  code: |
2323    ctx.fillStyle = '#f00';
2324    ctx.fillRect(0, 0, 100, 50);
2325
2326    ctx.fillStyle = '#0f0';
2327    ctx.moveTo(0, 0);
2328    ctx.lineTo(100, 0);
2329    ctx.lineTo(100, 50);
2330    ctx.lineTo(0, 50);
2331    ctx.fill();
2332
2333    @assert pixel 50,25 == 0,255,0,255;
2334  expected: green
2335
2336- name: 2d.path.fill.closed.unaffected
2337  testing:
2338  - 2d.path.fill.closed
2339  code: |
2340    ctx.fillStyle = '#00f';
2341    ctx.fillRect(0, 0, 100, 50);
2342
2343    ctx.moveTo(0, 0);
2344    ctx.lineTo(100, 0);
2345    ctx.lineTo(100, 50);
2346    ctx.fillStyle = '#f00';
2347    ctx.fill();
2348    ctx.lineTo(0, 50);
2349    ctx.fillStyle = '#0f0';
2350    ctx.fill();
2351
2352    @assert pixel 90,10 == 0,255,0,255;
2353    @assert pixel 10,40 == 0,255,0,255;
2354  expected: green
2355
2356- name: 2d.path.stroke.overlap
2357  desc: Stroked subpaths are combined before being drawn
2358  testing:
2359  - 2d.path.stroke.basic
2360  code: |
2361    ctx.fillStyle = '#000';
2362    ctx.fillRect(0, 0, 100, 50);
2363
2364    ctx.strokeStyle = 'rgba(0, 255, 0, 0.5)';
2365    ctx.lineWidth = 50;
2366    ctx.moveTo(0, 20);
2367    ctx.lineTo(100, 20);
2368    ctx.moveTo(0, 30);
2369    ctx.lineTo(100, 30);
2370    ctx.stroke();
2371
2372    @assert pixel 50,25 ==~ 0,127,0,255 +/- 1;
2373  expected: |
2374    size 100 50
2375    cr.set_source_rgb(0, 0.5, 0)
2376    cr.rectangle(0, 0, 100, 50)
2377    cr.fill()
2378
2379- name: 2d.path.stroke.union
2380  desc: Strokes in opposite directions are unioned, not subtracted
2381  testing:
2382  - 2d.path.stroke.basic
2383  code: |
2384    ctx.fillStyle = '#f00';
2385    ctx.fillRect(0, 0, 100, 50);
2386
2387    ctx.strokeStyle = '#0f0';
2388    ctx.lineWidth = 40;
2389    ctx.moveTo(0, 10);
2390    ctx.lineTo(100, 10);
2391    ctx.moveTo(100, 40);
2392    ctx.lineTo(0, 40);
2393    ctx.stroke();
2394
2395    @assert pixel 50,25 == 0,255,0,255;
2396  expected: green
2397
2398- name: 2d.path.stroke.unaffected
2399  desc: Stroking does not start a new path or subpath
2400  testing:
2401  - 2d.path.stroke.basic
2402  code: |
2403    ctx.fillStyle = '#f00';
2404    ctx.fillRect(0, 0, 100, 50);
2405
2406    ctx.lineWidth = 50;
2407    ctx.moveTo(-100, 25);
2408    ctx.lineTo(-100, -100);
2409    ctx.lineTo(200, -100);
2410    ctx.lineTo(200, 25);
2411    ctx.strokeStyle = '#f00';
2412    ctx.stroke();
2413
2414    ctx.closePath();
2415    ctx.strokeStyle = '#0f0';
2416    ctx.stroke();
2417
2418    @assert pixel 50,25 == 0,255,0,255;
2419  expected: green
2420
2421- name: 2d.path.stroke.scale1
2422  desc: Stroke line widths are scaled by the current transformation matrix
2423  testing:
2424  - 2d.path.transformation
2425  code: |
2426    ctx.fillStyle = '#f00';
2427    ctx.fillRect(0, 0, 100, 50);
2428
2429    ctx.beginPath();
2430    ctx.rect(25, 12.5, 50, 25);
2431    ctx.save();
2432    ctx.scale(50, 25);
2433    ctx.strokeStyle = '#0f0';
2434    ctx.stroke();
2435    ctx.restore();
2436
2437    ctx.beginPath();
2438    ctx.rect(-25, -12.5, 150, 75);
2439    ctx.save();
2440    ctx.scale(50, 25);
2441    ctx.strokeStyle = '#f00';
2442    ctx.stroke();
2443    ctx.restore();
2444
2445    @assert pixel 0,0 == 0,255,0,255;
2446    @assert pixel 50,0 == 0,255,0,255;
2447    @assert pixel 99,0 == 0,255,0,255;
2448    @assert pixel 0,25 == 0,255,0,255;
2449    @assert pixel 50,25 == 0,255,0,255;
2450    @assert pixel 99,25 == 0,255,0,255;
2451    @assert pixel 0,49 == 0,255,0,255;
2452    @assert pixel 50,49 == 0,255,0,255;
2453    @assert pixel 99,49 == 0,255,0,255;
2454  expected: green
2455
2456- name: 2d.path.stroke.scale2
2457  desc: Stroke line widths are scaled by the current transformation matrix
2458  testing:
2459  - 2d.path.transformation
2460  code: |
2461    ctx.fillStyle = '#f00';
2462    ctx.fillRect(0, 0, 100, 50);
2463
2464    ctx.beginPath();
2465    ctx.rect(25, 12.5, 50, 25);
2466    ctx.save();
2467    ctx.rotate(Math.PI/2);
2468    ctx.scale(25, 50);
2469    ctx.strokeStyle = '#0f0';
2470    ctx.stroke();
2471    ctx.restore();
2472
2473    ctx.beginPath();
2474    ctx.rect(-25, -12.5, 150, 75);
2475    ctx.save();
2476    ctx.rotate(Math.PI/2);
2477    ctx.scale(25, 50);
2478    ctx.strokeStyle = '#f00';
2479    ctx.stroke();
2480    ctx.restore();
2481
2482    @assert pixel 0,0 == 0,255,0,255;
2483    @assert pixel 50,0 == 0,255,0,255;
2484    @assert pixel 99,0 == 0,255,0,255;
2485    @assert pixel 0,25 == 0,255,0,255;
2486    @assert pixel 50,25 == 0,255,0,255;
2487    @assert pixel 99,25 == 0,255,0,255;
2488    @assert pixel 0,49 == 0,255,0,255;
2489    @assert pixel 50,49 == 0,255,0,255;
2490    @assert pixel 99,49 == 0,255,0,255;
2491  expected: green
2492
2493- name: 2d.path.stroke.skew
2494  desc: Strokes lines are skewed by the current transformation matrix
2495  testing:
2496  - 2d.path.transformation
2497  code: |
2498    ctx.fillStyle = '#f00';
2499    ctx.fillRect(0, 0, 100, 50);
2500
2501    ctx.save();
2502    ctx.beginPath();
2503    ctx.moveTo(49, -50);
2504    ctx.lineTo(201, -50);
2505    ctx.rotate(Math.PI/4);
2506    ctx.scale(1, 283);
2507    ctx.strokeStyle = '#0f0';
2508    ctx.stroke();
2509    ctx.restore();
2510
2511    ctx.save();
2512    ctx.beginPath();
2513    ctx.translate(-150, 0);
2514    ctx.moveTo(49, -50);
2515    ctx.lineTo(199, -50);
2516    ctx.rotate(Math.PI/4);
2517    ctx.scale(1, 142);
2518    ctx.strokeStyle = '#f00';
2519    ctx.stroke();
2520    ctx.restore();
2521
2522    ctx.save();
2523    ctx.beginPath();
2524    ctx.translate(-150, 0);
2525    ctx.moveTo(49, -50);
2526    ctx.lineTo(199, -50);
2527    ctx.rotate(Math.PI/4);
2528    ctx.scale(1, 142);
2529    ctx.strokeStyle = '#f00';
2530    ctx.stroke();
2531    ctx.restore();
2532
2533    @assert pixel 0,0 == 0,255,0,255;
2534    @assert pixel 50,0 == 0,255,0,255;
2535    @assert pixel 99,0 == 0,255,0,255;
2536    @assert pixel 0,25 == 0,255,0,255;
2537    @assert pixel 50,25 == 0,255,0,255;
2538    @assert pixel 99,25 == 0,255,0,255;
2539    @assert pixel 0,49 == 0,255,0,255;
2540    @assert pixel 50,49 == 0,255,0,255;
2541    @assert pixel 99,49 == 0,255,0,255;
2542  expected: green
2543
2544- name: 2d.path.stroke.empty
2545  desc: Empty subpaths are not stroked
2546  testing:
2547  - 2d.path.stroke.empty
2548  code: |
2549    ctx.fillStyle = '#0f0';
2550    ctx.fillRect(0, 0, 100, 50);
2551
2552    ctx.strokeStyle = '#f00';
2553    ctx.lineWidth = 100;
2554    ctx.lineCap = 'round';
2555    ctx.lineJoin = 'round';
2556
2557    ctx.beginPath();
2558    ctx.moveTo(40, 25);
2559    ctx.moveTo(60, 25);
2560    ctx.stroke();
2561
2562    @assert pixel 50,25 == 0,255,0,255;
2563  expected: green
2564
2565- name: 2d.path.stroke.prune.line
2566  desc: Zero-length line segments from lineTo are removed before stroking
2567  testing:
2568  - 2d.path.stroke.prune
2569  code: |
2570    ctx.fillStyle = '#0f0';
2571    ctx.fillRect(0, 0, 100, 50);
2572
2573    ctx.strokeStyle = '#f00';
2574    ctx.lineWidth = 100;
2575    ctx.lineCap = 'round';
2576    ctx.lineJoin = 'round';
2577
2578    ctx.beginPath();
2579    ctx.moveTo(50, 25);
2580    ctx.lineTo(50, 25);
2581    ctx.stroke();
2582
2583    @assert pixel 50,25 == 0,255,0,255; @moz-todo
2584  expected: green
2585
2586- name: 2d.path.stroke.prune.closed
2587  desc: Zero-length line segments from closed paths are removed before stroking
2588  testing:
2589  - 2d.path.stroke.prune
2590  code: |
2591    ctx.fillStyle = '#0f0';
2592    ctx.fillRect(0, 0, 100, 50);
2593
2594    ctx.strokeStyle = '#f00';
2595    ctx.lineWidth = 100;
2596    ctx.lineCap = 'round';
2597    ctx.lineJoin = 'round';
2598
2599    ctx.beginPath();
2600    ctx.moveTo(50, 25);
2601    ctx.lineTo(50, 25);
2602    ctx.closePath();
2603    ctx.stroke();
2604
2605    @assert pixel 50,25 == 0,255,0,255; @moz-todo
2606  expected: green
2607
2608- name: 2d.path.stroke.prune.curve
2609  desc: Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed
2610    before stroking
2611  testing:
2612  - 2d.path.stroke.prune
2613  code: |
2614    ctx.fillStyle = '#0f0';
2615    ctx.fillRect(0, 0, 100, 50);
2616
2617    ctx.strokeStyle = '#f00';
2618    ctx.lineWidth = 100;
2619    ctx.lineCap = 'round';
2620    ctx.lineJoin = 'round';
2621
2622    ctx.beginPath();
2623    ctx.moveTo(50, 25);
2624    ctx.quadraticCurveTo(50, 25, 50, 25);
2625    ctx.stroke();
2626
2627    ctx.beginPath();
2628    ctx.moveTo(50, 25);
2629    ctx.bezierCurveTo(50, 25, 50, 25, 50, 25);
2630    ctx.stroke();
2631
2632    @assert pixel 50,25 == 0,255,0,255; @moz-todo
2633  expected: green
2634
2635- name: 2d.path.stroke.prune.arc
2636  desc: Zero-length line segments from arcTo and arc are removed before stroking
2637  testing:
2638  - 2d.path.stroke.prune
2639  code: |
2640    ctx.fillStyle = '#0f0';
2641    ctx.fillRect(0, 0, 100, 50);
2642
2643    ctx.strokeStyle = '#f00';
2644    ctx.lineWidth = 100;
2645    ctx.lineCap = 'round';
2646    ctx.lineJoin = 'round';
2647
2648    ctx.beginPath();
2649    ctx.moveTo(50, 25);
2650    ctx.arcTo(50, 25, 150, 25, 10);
2651    ctx.stroke();
2652
2653    ctx.beginPath();
2654    ctx.moveTo(60, 25);
2655    ctx.arc(50, 25, 10, 0, 0, false);
2656    ctx.stroke();
2657
2658    @assert pixel 50,25 == 0,255,0,255; @moz-todo
2659  expected: green
2660
2661- name: 2d.path.stroke.prune.rect
2662  desc: Zero-length line segments from rect and strokeRect are removed before stroking
2663  testing:
2664  - 2d.path.stroke.prune
2665  code: |
2666    ctx.fillStyle = '#0f0';
2667    ctx.fillRect(0, 0, 100, 50);
2668
2669    ctx.strokeStyle = '#f00';
2670    ctx.lineWidth = 100;
2671    ctx.lineCap = 'round';
2672    ctx.lineJoin = 'round';
2673
2674    ctx.beginPath();
2675    ctx.rect(50, 25, 0, 0);
2676    ctx.stroke();
2677
2678    ctx.strokeRect(50, 25, 0, 0);
2679
2680    @assert pixel 50,25 == 0,255,0,255; @moz-todo
2681  expected: green
2682
2683- name: 2d.path.stroke.prune.corner
2684  desc: Zero-length line segments are removed before stroking with miters
2685  testing:
2686  - 2d.path.stroke.prune
2687  code: |
2688    ctx.fillStyle = '#0f0';
2689    ctx.fillRect(0, 0, 100, 50);
2690
2691    ctx.strokeStyle = '#f00';
2692    ctx.lineWidth = 400;
2693    ctx.lineJoin = 'miter';
2694    ctx.miterLimit = 1.4;
2695
2696    ctx.beginPath();
2697    ctx.moveTo(-1000, 200);
2698    ctx.lineTo(-100, 200);
2699    ctx.lineTo(-100, 200);
2700    ctx.lineTo(-100, 200);
2701    ctx.lineTo(-100, 1000);
2702    ctx.stroke();
2703
2704    @assert pixel 50,25 == 0,255,0,255;
2705  expected: green
2706
2707
2708- name: 2d.path.transformation.basic
2709  testing:
2710  - 2d.path.transformation
2711  code: |
2712    ctx.fillStyle = '#f00';
2713    ctx.fillRect(0, 0, 100, 50);
2714
2715    ctx.translate(-100, 0);
2716    ctx.rect(100, 0, 100, 50);
2717    ctx.translate(0, -100);
2718    ctx.fillStyle = '#0f0';
2719    ctx.fill();
2720
2721    @assert pixel 50,25 == 0,255,0,255;
2722  expected: green
2723
2724- name: 2d.path.transformation.multiple
2725  # TODO: change this name
2726  desc: Transformations are applied while building paths, not when drawing
2727  testing:
2728  - 2d.path.transformation
2729  code: |
2730    ctx.fillStyle = '#0f0';
2731    ctx.fillRect(0, 0, 100, 50);
2732
2733    ctx.fillStyle = '#f00';
2734    ctx.translate(-100, 0);
2735    ctx.rect(0, 0, 100, 50);
2736    ctx.fill();
2737    ctx.translate(100, 0);
2738    ctx.fill();
2739
2740    ctx.beginPath();
2741    ctx.strokeStyle = '#f00';
2742    ctx.lineWidth = 50;
2743    ctx.translate(0, -50);
2744    ctx.moveTo(0, 25);
2745    ctx.lineTo(100, 25);
2746    ctx.stroke();
2747    ctx.translate(0, 50);
2748    ctx.stroke();
2749
2750    @assert pixel 50,25 == 0,255,0,255;
2751  expected: green
2752
2753- name: 2d.path.transformation.changing
2754  desc: Transformations are applied while building paths, not when drawing
2755  testing:
2756  - 2d.path.transformation
2757  code: |
2758    ctx.fillStyle = '#f00';
2759    ctx.fillRect(0, 0, 100, 50);
2760    ctx.fillStyle = '#0f0';
2761    ctx.moveTo(0, 0);
2762    ctx.translate(100, 0);
2763    ctx.lineTo(0, 0);
2764    ctx.translate(0, 50);
2765    ctx.lineTo(0, 0);
2766    ctx.translate(-100, 0);
2767    ctx.lineTo(0, 0);
2768    ctx.translate(1000, 1000);
2769    ctx.rotate(Math.PI/2);
2770    ctx.scale(0.1, 0.1);
2771    ctx.fill();
2772
2773    @assert pixel 50,25 == 0,255,0,255;
2774  expected: green
2775
2776
2777- name: 2d.path.clip.empty
2778  testing:
2779  - 2d.path.clip.basic
2780  code: |
2781    ctx.fillStyle = '#0f0';
2782    ctx.fillRect(0, 0, 100, 50);
2783
2784    ctx.beginPath();
2785    ctx.clip();
2786
2787    ctx.fillStyle = '#f00';
2788    ctx.fillRect(0, 0, 100, 50);
2789
2790    @assert pixel 50,25 == 0,255,0,255;
2791  expected: green
2792
2793- name: 2d.path.clip.basic.1
2794  testing:
2795  - 2d.path.clip.basic
2796  code: |
2797    ctx.fillStyle = '#f00';
2798    ctx.fillRect(0, 0, 100, 50);
2799
2800    ctx.beginPath();
2801    ctx.rect(0, 0, 100, 50);
2802    ctx.clip();
2803
2804    ctx.fillStyle = '#0f0';
2805    ctx.fillRect(0, 0, 100, 50);
2806
2807    @assert pixel 50,25 == 0,255,0,255;
2808  expected: green
2809
2810- name: 2d.path.clip.basic.2
2811  testing:
2812  - 2d.path.clip.basic
2813  code: |
2814    ctx.fillStyle = '#0f0';
2815    ctx.fillRect(0, 0, 100, 50);
2816
2817    ctx.beginPath();
2818    ctx.rect(-100, 0, 100, 50);
2819    ctx.clip();
2820
2821    ctx.fillStyle = '#f00';
2822    ctx.fillRect(0, 0, 100, 50);
2823
2824    @assert pixel 50,25 == 0,255,0,255;
2825  expected: green
2826
2827- name: 2d.path.clip.intersect
2828  testing:
2829  - 2d.path.clip.basic
2830  code: |
2831    ctx.fillStyle = '#0f0';
2832    ctx.fillRect(0, 0, 100, 50);
2833
2834    ctx.beginPath();
2835    ctx.rect(0, 0, 50, 50);
2836    ctx.clip();
2837    ctx.beginPath();
2838    ctx.rect(50, 0, 50, 50)
2839    ctx.clip();
2840
2841    ctx.fillStyle = '#f00';
2842    ctx.fillRect(0, 0, 100, 50);
2843
2844    @assert pixel 50,25 == 0,255,0,255;
2845  expected: green
2846
2847- name: 2d.path.clip.winding.1
2848  testing:
2849  - 2d.path.clip.basic
2850  code: |
2851    ctx.fillStyle = '#0f0';
2852    ctx.fillRect(0, 0, 100, 50);
2853
2854    ctx.beginPath();
2855    ctx.moveTo(-10, -10);
2856    ctx.lineTo(110, -10);
2857    ctx.lineTo(110, 60);
2858    ctx.lineTo(-10, 60);
2859    ctx.lineTo(-10, -10);
2860    ctx.lineTo(0, 0);
2861    ctx.lineTo(0, 50);
2862    ctx.lineTo(100, 50);
2863    ctx.lineTo(100, 0);
2864    ctx.clip();
2865
2866    ctx.fillStyle = '#f00';
2867    ctx.fillRect(0, 0, 100, 50);
2868
2869    @assert pixel 50,25 == 0,255,0,255;
2870  expected: green
2871
2872- name: 2d.path.clip.winding.2
2873  testing:
2874  - 2d.path.clip.basic
2875  code: |
2876    ctx.fillStyle = '#f00';
2877    ctx.fillRect(0, 0, 100, 50);
2878
2879    ctx.beginPath();
2880    ctx.moveTo(-10, -10);
2881    ctx.lineTo(110, -10);
2882    ctx.lineTo(110, 60);
2883    ctx.lineTo(-10, 60);
2884    ctx.lineTo(-10, -10);
2885    ctx.clip();
2886
2887    ctx.beginPath();
2888    ctx.moveTo(0, 0);
2889    ctx.lineTo(0, 50);
2890    ctx.lineTo(100, 50);
2891    ctx.lineTo(100, 0);
2892    ctx.lineTo(0, 0);
2893    ctx.clip();
2894
2895    ctx.fillStyle = '#0f0';
2896    ctx.fillRect(0, 0, 100, 50);
2897
2898    @assert pixel 50,25 == 0,255,0,255;
2899  expected: green
2900
2901- name: 2d.path.clip.unaffected
2902  testing:
2903  - 2d.path.clip.closed
2904  code: |
2905    ctx.fillStyle = '#f00';
2906    ctx.fillRect(0, 0, 100, 50);
2907
2908    ctx.fillStyle = '#0f0';
2909
2910    ctx.beginPath();
2911    ctx.moveTo(0, 0);
2912    ctx.lineTo(0, 50);
2913    ctx.lineTo(100, 50);
2914    ctx.lineTo(100, 0);
2915    ctx.clip();
2916
2917    ctx.lineTo(0, 0);
2918    ctx.fill();
2919
2920    @assert pixel 50,25 == 0,255,0,255;
2921  expected: green
2922
2923
2924
2925- name: 2d.path.isPointInPath.basic.1
2926  desc: isPointInPath() detects whether the point is inside the path
2927  testing:
2928  - 2d.path.isPointInPath
2929  code: |
2930    ctx.rect(0, 0, 20, 20);
2931    @assert ctx.isPointInPath(10, 10) === true;
2932    @assert ctx.isPointInPath(30, 10) === false;
2933
2934- name: 2d.path.isPointInPath.basic.2
2935  desc: isPointInPath() detects whether the point is inside the path
2936  testing:
2937  - 2d.path.isPointInPath
2938  code: |
2939    ctx.rect(20, 0, 20, 20);
2940    @assert ctx.isPointInPath(10, 10) === false;
2941    @assert ctx.isPointInPath(30, 10) === true;
2942
2943- name: 2d.path.isPointInPath.edge
2944  desc: isPointInPath() counts points on the path as being inside
2945  testing:
2946  - 2d.path.isPointInPath.edge
2947  code: |
2948    ctx.rect(0, 0, 20, 20);
2949    @assert ctx.isPointInPath(0, 0) === true;
2950    @assert ctx.isPointInPath(10, 0) === true;
2951    @assert ctx.isPointInPath(20, 0) === true;
2952    @assert ctx.isPointInPath(20, 10) === true;
2953    @assert ctx.isPointInPath(20, 20) === true;
2954    @assert ctx.isPointInPath(10, 20) === true;
2955    @assert ctx.isPointInPath(0, 20) === true;
2956    @assert ctx.isPointInPath(0, 10) === true;
2957    @assert ctx.isPointInPath(10, -0.01) === false;
2958    @assert ctx.isPointInPath(10, 20.01) === false;
2959    @assert ctx.isPointInPath(-0.01, 10) === false;
2960    @assert ctx.isPointInPath(20.01, 10) === false;
2961
2962- name: 2d.path.isPointInPath.empty
2963  desc: isPointInPath() works when there is no path
2964  testing:
2965  - 2d.path.isPointInPath
2966  code: |
2967    @assert ctx.isPointInPath(0, 0) === false;
2968
2969- name: 2d.path.isPointInPath.subpath
2970  desc: isPointInPath() uses the current path, not just the subpath
2971  testing:
2972  - 2d.path.isPointInPath
2973  code: |
2974    ctx.rect(0, 0, 20, 20);
2975    ctx.beginPath();
2976    ctx.rect(20, 0, 20, 20);
2977    ctx.closePath();
2978    ctx.rect(40, 0, 20, 20);
2979    @assert ctx.isPointInPath(10, 10) === false;
2980    @assert ctx.isPointInPath(30, 10) === true;
2981    @assert ctx.isPointInPath(50, 10) === true;
2982
2983- name: 2d.path.isPointInPath.outside
2984  desc: isPointInPath() works on paths outside the canvas
2985  testing:
2986  - 2d.path.isPointInPath
2987  code: |
2988    ctx.rect(0, -100, 20, 20);
2989    ctx.rect(20, -10, 20, 20);
2990    @assert ctx.isPointInPath(10, -110) === false;
2991    @assert ctx.isPointInPath(10, -90) === true;
2992    @assert ctx.isPointInPath(10, -70) === false;
2993    @assert ctx.isPointInPath(30, -20) === false;
2994    @assert ctx.isPointInPath(30, 0) === true;
2995    @assert ctx.isPointInPath(30, 20) === false;
2996
2997- name: 2d.path.isPointInPath.unclosed
2998  desc: isPointInPath() works on unclosed subpaths
2999  testing:
3000  - 2d.path.isPointInPath
3001  code: |
3002    ctx.moveTo(0, 0);
3003    ctx.lineTo(20, 0);
3004    ctx.lineTo(20, 20);
3005    ctx.lineTo(0, 20);
3006    @assert ctx.isPointInPath(10, 10) === true;
3007    @assert ctx.isPointInPath(30, 10) === false;
3008
3009- name: 2d.path.isPointInPath.arc
3010  desc: isPointInPath() works on arcs
3011  testing:
3012  - 2d.path.isPointInPath
3013  code: |
3014    ctx.arc(50, 25, 10, 0, Math.PI, false);
3015    @assert ctx.isPointInPath(50, 10) === false;
3016    @assert ctx.isPointInPath(50, 20) === false;
3017    @assert ctx.isPointInPath(50, 30) === true;
3018    @assert ctx.isPointInPath(50, 40) === false;
3019    @assert ctx.isPointInPath(30, 20) === false;
3020    @assert ctx.isPointInPath(70, 20) === false;
3021    @assert ctx.isPointInPath(30, 30) === false;
3022    @assert ctx.isPointInPath(70, 30) === false;
3023
3024- name: 2d.path.isPointInPath.bigarc
3025  desc: isPointInPath() works on unclosed arcs larger than 2pi
3026  opera: {bug: 320937}
3027  testing:
3028  - 2d.path.isPointInPath
3029  code: |
3030    ctx.arc(50, 25, 10, 0, 7, false);
3031    @assert ctx.isPointInPath(50, 10) === false;
3032    @assert ctx.isPointInPath(50, 20) === true;
3033    @assert ctx.isPointInPath(50, 30) === true;
3034    @assert ctx.isPointInPath(50, 40) === false;
3035    @assert ctx.isPointInPath(30, 20) === false;
3036    @assert ctx.isPointInPath(70, 20) === false;
3037    @assert ctx.isPointInPath(30, 30) === false;
3038    @assert ctx.isPointInPath(70, 30) === false;
3039
3040- name: 2d.path.isPointInPath.bezier
3041  desc: isPointInPath() works on Bezier curves
3042  testing:
3043  - 2d.path.isPointInPath
3044  code: |
3045    ctx.moveTo(25, 25);
3046    ctx.bezierCurveTo(50, -50, 50, 100, 75, 25);
3047    @assert ctx.isPointInPath(25, 20) === false;
3048    @assert ctx.isPointInPath(25, 30) === false;
3049    @assert ctx.isPointInPath(30, 20) === true;
3050    @assert ctx.isPointInPath(30, 30) === false;
3051    @assert ctx.isPointInPath(40, 2) === false;
3052    @assert ctx.isPointInPath(40, 20) === true;
3053    @assert ctx.isPointInPath(40, 30) === false;
3054    @assert ctx.isPointInPath(40, 47) === false;
3055    @assert ctx.isPointInPath(45, 20) === true;
3056    @assert ctx.isPointInPath(45, 30) === false;
3057    @assert ctx.isPointInPath(55, 20) === false;
3058    @assert ctx.isPointInPath(55, 30) === true;
3059    @assert ctx.isPointInPath(60, 2) === false;
3060    @assert ctx.isPointInPath(60, 20) === false;
3061    @assert ctx.isPointInPath(60, 30) === true;
3062    @assert ctx.isPointInPath(60, 47) === false;
3063    @assert ctx.isPointInPath(70, 20) === false;
3064    @assert ctx.isPointInPath(70, 30) === true;
3065    @assert ctx.isPointInPath(75, 20) === false;
3066    @assert ctx.isPointInPath(75, 30) === false;
3067
3068- name: 2d.path.isPointInPath.winding
3069  desc: isPointInPath() uses the non-zero winding number rule
3070  testing:
3071  - 2d.path.isPointInPath
3072  code: |
3073    // Create a square ring, using opposite windings to make a hole in the centre
3074    ctx.moveTo(0, 0);
3075    ctx.lineTo(50, 0);
3076    ctx.lineTo(50, 50);
3077    ctx.lineTo(0, 50);
3078    ctx.lineTo(0, 0);
3079    ctx.lineTo(10, 10);
3080    ctx.lineTo(10, 40);
3081    ctx.lineTo(40, 40);
3082    ctx.lineTo(40, 10);
3083    ctx.lineTo(10, 10);
3084
3085    @assert ctx.isPointInPath(5, 5) === true;
3086    @assert ctx.isPointInPath(25, 5) === true;
3087    @assert ctx.isPointInPath(45, 5) === true;
3088    @assert ctx.isPointInPath(5, 25) === true;
3089    @assert ctx.isPointInPath(25, 25) === false;
3090    @assert ctx.isPointInPath(45, 25) === true;
3091    @assert ctx.isPointInPath(5, 45) === true;
3092    @assert ctx.isPointInPath(25, 45) === true;
3093    @assert ctx.isPointInPath(45, 45) === true;
3094
3095- name: 2d.path.isPointInPath.transform.1
3096  desc: isPointInPath() handles transformations correctly
3097  testing:
3098  - 2d.path.isPointInPath
3099  code: |
3100    ctx.translate(50, 0);
3101    ctx.rect(0, 0, 20, 20);
3102    @assert ctx.isPointInPath(-40, 10) === false;
3103    @assert ctx.isPointInPath(10, 10) === false;
3104    @assert ctx.isPointInPath(49, 10) === false;
3105    @assert ctx.isPointInPath(51, 10) === true;
3106    @assert ctx.isPointInPath(69, 10) === true;
3107    @assert ctx.isPointInPath(71, 10) === false;
3108
3109- name: 2d.path.isPointInPath.transform.2
3110  desc: isPointInPath() handles transformations correctly
3111  testing:
3112  - 2d.path.isPointInPath
3113  code: |
3114    ctx.rect(50, 0, 20, 20);
3115    ctx.translate(50, 0);
3116    @assert ctx.isPointInPath(-40, 10) === false;
3117    @assert ctx.isPointInPath(10, 10) === false;
3118    @assert ctx.isPointInPath(49, 10) === false;
3119    @assert ctx.isPointInPath(51, 10) === true;
3120    @assert ctx.isPointInPath(69, 10) === true;
3121    @assert ctx.isPointInPath(71, 10) === false;
3122
3123- name: 2d.path.isPointInPath.transform.3
3124  desc: isPointInPath() handles transformations correctly
3125  testing:
3126  - 2d.path.isPointInPath
3127  code: |
3128    ctx.scale(-1, 1);
3129    ctx.rect(-70, 0, 20, 20);
3130    @assert ctx.isPointInPath(-40, 10) === false;
3131    @assert ctx.isPointInPath(10, 10) === false;
3132    @assert ctx.isPointInPath(49, 10) === false;
3133    @assert ctx.isPointInPath(51, 10) === true;
3134    @assert ctx.isPointInPath(69, 10) === true;
3135    @assert ctx.isPointInPath(71, 10) === false;
3136
3137- name: 2d.path.isPointInPath.transform.4
3138  desc: isPointInPath() handles transformations correctly
3139  testing:
3140  - 2d.path.isPointInPath
3141  code: |
3142    ctx.translate(50, 0);
3143    ctx.rect(50, 0, 20, 20);
3144    ctx.translate(0, 50);
3145    @assert ctx.isPointInPath(60, 10) === false;
3146    @assert ctx.isPointInPath(110, 10) === true;
3147    @assert ctx.isPointInPath(110, 60) === false;
3148
3149- name: 2d.path.isPointInPath.nonfinite
3150  desc: isPointInPath() returns false for non-finite arguments
3151  testing:
3152  - 2d.path.isPointInPath.nonfinite
3153  code: |
3154    ctx.rect(-100, -50, 200, 100);
3155    @assert ctx.isPointInPath(Infinity, 0) === false;
3156    @assert ctx.isPointInPath(-Infinity, 0) === false;
3157    @assert ctx.isPointInPath(NaN, 0) === false;
3158    @assert ctx.isPointInPath(0, Infinity) === false;
3159    @assert ctx.isPointInPath(0, -Infinity) === false;
3160    @assert ctx.isPointInPath(0, NaN) === false;
3161    @assert ctx.isPointInPath(NaN, NaN) === false;
3162
3163
3164- name: 2d.path.isPointInStroke.scaleddashes
3165  desc: isPointInStroke() should return correct results on dashed paths at high scale
3166    factors
3167  testing:
3168  - 2d.path.isPointInStroke
3169  code: |
3170    var scale = 20;
3171    ctx.setLineDash([10, 21.4159]); // dash from t=0 to t=10 along the circle
3172    ctx.scale(scale, scale);
3173    ctx.ellipse(6, 10, 5, 5, 0, 2*Math.PI, false);
3174    ctx.stroke();
3175
3176    // hit-test the beginning of the dash (t=0)
3177    @assert ctx.isPointInStroke(11*scale, 10*scale) === true;
3178    // hit-test the middle of the dash (t=5)
3179    @assert ctx.isPointInStroke(8.70*scale, 14.21*scale) === true;
3180    // hit-test the end of the dash (t=9.8)
3181    @assert ctx.isPointInStroke(4.10*scale, 14.63*scale) === true;
3182    // hit-test past the end of the dash (t=10.2)
3183    @assert ctx.isPointInStroke(3.74*scale, 14.46*scale) === false;
3184
3185- name: 2d.path.isPointInPath.basic
3186  desc: Verify the winding rule in isPointInPath works for for rect path.
3187  testing:
3188  - 2d.isPointInPath.basic
3189  code: |
3190    canvas.width = 200;
3191    canvas.height = 200;
3192
3193    // Testing default isPointInPath
3194    ctx.beginPath();
3195    ctx.rect(0, 0, 100, 100);
3196    ctx.rect(25, 25, 50, 50);
3197    @assert ctx.isPointInPath(50, 50) === true;
3198    @assert ctx.isPointInPath(NaN, 50) === false;
3199    @assert ctx.isPointInPath(50, NaN) === false;
3200
3201    // Testing nonzero isPointInPath
3202    ctx.beginPath();
3203    ctx.rect(0, 0, 100, 100);
3204    ctx.rect(25, 25, 50, 50);
3205    @assert ctx.isPointInPath(50, 50, 'nonzero') === true;
3206
3207    // Testing evenodd isPointInPath
3208    ctx.beginPath();
3209    ctx.rect(0, 0, 100, 100);
3210    ctx.rect(25, 25, 50, 50);
3211    @assert ctx.isPointInPath(50, 50, 'evenodd') === false;
3212
3213    // Testing extremely large scale
3214    ctx.save();
3215    ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
3216    ctx.beginPath();
3217    ctx.rect(-10, -10, 20, 20);
3218    @assert ctx.isPointInPath(0, 0, 'nonzero') === true;
3219    @assert ctx.isPointInPath(0, 0, 'evenodd') === true;
3220    ctx.restore();
3221
3222    // Check with non-invertible ctm.
3223    ctx.save();
3224    ctx.scale(0, 0);
3225    ctx.beginPath();
3226    ctx.rect(-10, -10, 20, 20);
3227    @assert ctx.isPointInPath(0, 0, 'nonzero') === false;
3228    @assert ctx.isPointInPath(0, 0, 'evenodd') === false;
3229    ctx.restore();
3230
3231- name: 2d.path.isPointInpath.multi.path
3232  desc: Verify the winding rule in isPointInPath works for path object.
3233  testing:
3234  - 2d.isPointInPath.basic
3235  code: |
3236    canvas.width = 200;
3237    canvas.height = 200;
3238
3239    // Testing default isPointInPath with Path object');
3240    path = new Path2D();
3241    path.rect(0, 0, 100, 100);
3242    path.rect(25, 25, 50, 50);
3243    @assert ctx.isPointInPath(path, 50, 50) === true;
3244    @assert ctx.isPointInPath(path, 50, 50, undefined) === true;
3245    @assert ctx.isPointInPath(path, NaN, 50) === false;
3246    @assert ctx.isPointInPath(path, 50, NaN) === false;
3247
3248    // Testing nonzero isPointInPath with Path object');
3249    path = new Path2D();
3250    path.rect(0, 0, 100, 100);
3251    path.rect(25, 25, 50, 50);
3252    @assert ctx.isPointInPath(path, 50, 50, 'nonzero') === true;
3253
3254    // Testing evenodd isPointInPath with Path object');
3255    path = new Path2D();
3256    path.rect(0, 0, 100, 100);
3257    path.rect(25, 25, 50, 50);
3258    assert_false(ctx.isPointInPath(path, 50, 50, 'evenodd'));
3259
3260- name: 2d.path.isPointInpath.invalid
3261  desc: Verify isPointInPath throws exceptions with invalid inputs.
3262  testing:
3263  - 2d.isPointInPath.basic
3264  code: |
3265    canvas.width = 200;
3266    canvas.height = 200;
3267    path = new Path2D();
3268    path.rect(0, 0, 100, 100);
3269    path.rect(25, 25, 50, 50);
3270    // Testing invalid enumeration isPointInPath (w/ and w/o Path object');
3271    @assert throws TypeError ctx.isPointInPath(path, 50, 50, 'gazonk');
3272    @assert throws TypeError ctx.isPointInPath(50, 50, 'gazonk');
3273
3274    // Testing invalid type isPointInPath with Path object');
3275    @assert throws TypeError ctx.isPointInPath(null, 50, 50);
3276    @assert throws TypeError ctx.isPointInPath(null, 50, 50, 'nonzero');
3277    @assert throws TypeError ctx.isPointInPath(null, 50, 50, 'evenodd');
3278    @assert throws TypeError ctx.isPointInPath(null, 50, 50, null);
3279    @assert throws TypeError ctx.isPointInPath(path, 50, 50, null);
3280    @assert throws TypeError ctx.isPointInPath(undefined, 50, 50);
3281    @assert throws TypeError ctx.isPointInPath(undefined, 50, 50, 'nonzero');
3282    @assert throws TypeError ctx.isPointInPath(undefined, 50, 50, 'evenodd');
3283    @assert throws TypeError ctx.isPointInPath(undefined, 50, 50, undefined);
3284    @assert throws TypeError ctx.isPointInPath([], 50, 50);
3285    @assert throws TypeError ctx.isPointInPath([], 50, 50, 'nonzero');
3286    @assert throws TypeError ctx.isPointInPath([], 50, 50, 'evenodd');
3287    @assert throws TypeError ctx.isPointInPath({}, 50, 50);
3288    @assert throws TypeError ctx.isPointInPath({}, 50, 50, 'nonzero');
3289    @assert throws TypeError ctx.isPointInPath({}, 50, 50, 'evenodd');
3290