xref: /aosp_15_r20/external/skia/tests/PathOpsAsWindingTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkPath.h"
9 #include "include/core/SkPathTypes.h"
10 #include "include/core/SkPoint.h"
11 #include "include/core/SkRect.h"
12 #include "include/core/SkScalar.h"
13 #include "include/core/SkTypes.h"
14 #include "include/pathops/SkPathOps.h"
15 #include "include/utils/SkParsePath.h"
16 #include "tests/Test.h"
17 
18 #include <initializer_list>
19 #include <string>
20 
build_squircle(SkPath::Verb verb,const SkRect & rect,SkPathDirection dir)21 static SkPath build_squircle(SkPath::Verb verb, const SkRect& rect, SkPathDirection dir) {
22     SkPath path;
23     bool reverse = SkPathDirection::kCCW == dir;
24     switch (verb) {
25         case SkPath::kLine_Verb:
26             path.addRect(rect, dir);
27             reverse = false;
28             break;
29         case SkPath::kQuad_Verb:
30             path.moveTo(rect.centerX(), rect.fTop);
31             path.quadTo(rect.fRight, rect.fTop, rect.fRight, rect.centerY());
32             path.quadTo(rect.fRight, rect.fBottom, rect.centerX(), rect.fBottom);
33             path.quadTo(rect.fLeft, rect.fBottom, rect.fLeft, rect.centerY());
34             path.quadTo(rect.fLeft, rect.fTop, rect.centerX(), rect.fTop);
35             break;
36         case SkPath::kConic_Verb:
37             path.addCircle(rect.centerX(), rect.centerY(), rect.width() / 2, dir);
38             reverse = false;
39             break;
40         case SkPath::kCubic_Verb: {
41             SkScalar aX14 = rect.fLeft + rect.width() * 1 / 4;
42             SkScalar aX34 = rect.fLeft + rect.width() * 3 / 4;
43             SkScalar aY14 = rect.fTop + rect.height() * 1 / 4;
44             SkScalar aY34 = rect.fTop + rect.height() * 3 / 4;
45             path.moveTo(rect.centerX(), rect.fTop);
46             path.cubicTo(aX34, rect.fTop, rect.fRight, aY14, rect.fRight, rect.centerY());
47             path.cubicTo(rect.fRight, aY34, aX34, rect.fBottom, rect.centerX(), rect.fBottom);
48             path.cubicTo(aX14, rect.fBottom, rect.fLeft, aY34, rect.fLeft, rect.centerY());
49             path.cubicTo(rect.fLeft, aY14, aX14, rect.fTop, rect.centerX(), rect.fTop);
50             } break;
51         default:
52             SkASSERT(0);
53     }
54     if (reverse) {
55         SkPath temp;
56         temp.reverseAddPath(path);
57         path.swap(temp);
58     }
59     return path;
60 }
61 
bug12040_1(skiatest::Reporter * reporter)62 void bug12040_1(skiatest::Reporter* reporter) {
63     SkPath path;
64     path.setFillType(SkPathFillType::kWinding);
65     path.moveTo(375, -30);
66     path.cubicTo(578, -30, 749, 176, 749, 422);
67     path.cubicTo(749, 583, 666, 706, 518, 765);
68     path.lineTo(163, 611);
69     path.lineTo(163, 579);
70     path.lineTo(405, 684);
71     path.cubicTo(551, 609, 645, 468, 645, 322);
72     path.cubicTo(645, 183, 563, 82, 450, 82);
73     path.cubicTo(303, 82, 179, 249, 179, 446);
74     path.cubicTo(179, 579, 235, 689, 341, 768);
75     path.lineTo(327, 786);
76     path.cubicTo(165, 717, 56, 536, 56, 335);
77     path.cubicTo(56, 125, 192, -30, 375, -30);
78     path.close();
79     path.moveTo(214, 225);
80     path.cubicTo(333, 248, 396, 311, 396, 405);
81     path.lineTo(396, 695);
82     path.lineTo(267, 641);
83     path.lineTo(267, 395);
84     path.cubicTo(267, 324, 249, 285, 201, 254);
85     path.cubicTo(201, 254, 214, 225, 214, 225);
86     path.close();
87     path.moveTo(682, -106);
88     path.lineTo(832, 12);
89     path.lineTo(813, 33);
90     path.lineTo(772, 0);
91     path.cubicTo(716, 29, 668, 76, 628, 140);
92     path.lineTo(527, 44);
93     path.cubicTo(575, -26, 628, -77, 682, -106);
94     path.close();
95     path.moveTo(450, 59);
96     path.lineTo(480, 59);
97     path.lineTo(480, 678);
98     path.lineTo(450, 678);
99     path.cubicTo(450, 678, 450, 59, 450, 59);
100     path.close();
101     path.moveTo(463, 374);
102     path.lineTo(633, 459);
103     path.lineTo(633, 490);
104     path.lineTo(463, 406);
105     path.cubicTo(463, 406, 463, 374, 463, 374);
106     path.close();
107     path.moveTo(463, 269);
108     path.lineTo(667, 372);
109     path.lineTo(667, 403);
110     path.lineTo(463, 301);
111     path.cubicTo(463, 301, 463, 269, 463, 269);
112     path.close();
113 
114     SkPath path2;
115     path2.setFillType(SkPathFillType::kWinding);
116     path2.moveTo(-83.5464f, 188);
117     path2.cubicTo(-83.5464f, 184.285f, -84.8599f, 181.114f, -87.4868f, 178.487f);
118     path2.cubicTo(-90.1138f, 175.86f, -93.2849f, 174.546f, -97, 174.546f);
119     path2.cubicTo(-100.715f, 174.546f, -103.886f, 175.86f, -106.513f, 178.487f);
120     path2.cubicTo(-109.14f, 181.114f, -110.454f, 184.285f, -110.454f, 188);
121     path2.cubicTo(-110.454f, 191.715f, -109.14f, 194.886f, -106.513f, 197.513f);
122     path2.cubicTo(-103.886f, 200.14f, -100.715f, 201.454f, -97, 201.454f);
123     path2.cubicTo(-93.2849f, 201.454f, -90.1138f, 200.14f, -87.4868f, 197.513f);
124     path2.cubicTo(-84.8599f, 194.886f, -83.5464f, 191.715f, -83.5464f, 188);
125     path2.close();
126 
127     SkPath opResult;
128     Op(path, path2, kDifference_SkPathOp, &opResult);
129 
130     SkPath winding;
131     REPORTER_ASSERT(reporter, AsWinding(opResult, &winding));
132     REPORTER_ASSERT(reporter, winding.getFillType() == SkPathFillType::kWinding);
133 
134     SkPath difference;
135     Op(winding, opResult, kXOR_SkPathOp, &difference);
136     REPORTER_ASSERT(reporter, difference.isEmpty());
137 }
138 
bug12040_2(skiatest::Reporter * reporter)139 void bug12040_2(skiatest::Reporter* reporter) {
140     SkPath path;
141     path.setFillType(SkPathFillType::kWinding);
142     path.moveTo(375, -30);
143     path.cubicTo(578, -30, 749, 176, 749, 422);
144     path.cubicTo(749, 583, 666, 706, 518, 765);
145     path.lineTo(163, 611);
146     path.lineTo(163, 579);
147     path.lineTo(405, 684);
148     path.cubicTo(551, 609, 645, 468, 645, 322);
149     path.cubicTo(645, 183, 563, 82, 450, 82);
150     path.cubicTo(303, 82, 179, 249, 179, 446);
151     path.cubicTo(179, 579, 235, 689, 341, 768);
152     path.lineTo(327, 786);
153     path.cubicTo(165, 717, 56, 536, 56, 335);
154     path.cubicTo(56, 125, 192, -30, 375, -30);
155     path.close();
156     path.moveTo(214, 225);
157     path.cubicTo(333, 248, 396, 311, 396, 405);
158     path.lineTo(396, 695);
159     path.lineTo(267, 641);
160     path.lineTo(267, 395);
161     path.cubicTo(267, 324, 249, 285, 201, 254);
162     path.cubicTo(201, 254, 214, 225, 214, 225);
163     path.close();
164     path.moveTo(682, -106);
165     path.lineTo(832, 12);
166     path.lineTo(813, 33);
167     path.lineTo(772, 0);
168     path.cubicTo(716, 29, 668, 76, 628, 140);
169     path.lineTo(527, 44);
170     path.cubicTo(575, -26, 628, -77, 682, -106);
171     path.close();
172     path.moveTo(450, 59);
173     path.lineTo(480, 59);
174     path.lineTo(480, 678);
175     path.lineTo(450, 678);
176     path.cubicTo(450, 678, 450, 59, 450, 59);
177     path.close();
178     path.moveTo(463, 374);
179     path.lineTo(633, 459);
180     path.lineTo(633, 490);
181     path.lineTo(463, 406);
182     path.cubicTo(463, 406, 463, 374, 463, 374);
183     path.close();
184     path.moveTo(463, 269);
185     path.lineTo(667, 372);
186     path.lineTo(667, 403);
187     path.lineTo(463, 301);
188     path.cubicTo(463, 301, 463, 269, 463, 269);
189     path.close();
190 
191     SkPath path2;
192     path2.setFillType(SkPathFillType::kWinding);
193     path2.moveTo(269.134f, 71.3392f);
194     path2.cubicTo(269.134f, 67.6241f, 267.82f, 64.453f, 265.193f, 61.826f);
195     path2.cubicTo(262.566f, 59.1991f, 259.395f, 57.8856f, 255.68f, 57.8856f);
196     path2.cubicTo(251.965f, 57.8856f, 248.794f, 59.1991f, 246.167f, 61.826f);
197     path2.cubicTo(243.54f, 64.453f, 242.226f, 67.6241f, 242.226f, 71.3392f);
198     path2.cubicTo(242.226f, 75.0543f, 243.54f, 78.2255f, 246.167f, 80.8524f);
199     path2.cubicTo(248.794f, 83.4794f, 251.965f, 84.7928f, 255.68f, 84.7928f);
200     path2.cubicTo(259.395f, 84.7928f, 262.566f, 83.4794f, 265.193f, 80.8524f);
201     path2.cubicTo(267.82f, 78.2255f, 269.134f, 75.0543f, 269.134f, 71.3392f);
202     path2.close();
203 
204     SkPath opResult;
205     Op(path, path2, kDifference_SkPathOp, &opResult);
206 
207     // This produces the correct result in all the cases I've tried.
208     // So it appears to be a winding issue.
209     // canvas->drawPath(opResult, p);
210 
211     SkPath winding;
212     path2.setFillType(SkPathFillType::kWinding);
213     REPORTER_ASSERT(reporter, AsWinding(opResult, &winding));
214     REPORTER_ASSERT(reporter, winding.getFillType() == SkPathFillType::kWinding);
215 
216     SkPath difference;
217     Op(winding, opResult, kXOR_SkPathOp, &difference);
218     REPORTER_ASSERT(reporter, difference.isEmpty());
219 }
220 
bug12040_3(skiatest::Reporter * reporter)221 void bug12040_3(skiatest::Reporter* reporter) {
222     SkPath path;
223     path.setFillType(SkPathFillType::kWinding);
224     path.moveTo(375, -30);
225     path.cubicTo(578, -30, 749, 176, 749, 422);
226     path.cubicTo(749, 583, 666, 706, 518, 765);
227     path.lineTo(163, 611);
228     path.lineTo(163, 579);
229     path.lineTo(405, 684);
230     path.cubicTo(551, 609, 645, 468, 645, 322);
231     path.cubicTo(645, 183, 563, 82, 450, 82);
232     path.cubicTo(303, 82, 179, 249, 179, 446);
233     path.cubicTo(179, 579, 235, 689, 341, 768);
234     path.lineTo(327, 786);
235     path.cubicTo(165, 717, 56, 536, 56, 335);
236     path.cubicTo(56, 125, 192, -30, 375, -30);
237     path.close();
238     path.moveTo(214, 225);
239     path.cubicTo(333, 248, 396, 311, 396, 405);
240     path.lineTo(396, 695);
241     path.lineTo(267, 641);
242     path.lineTo(267, 395);
243     path.cubicTo(267, 324, 249, 285, 201, 254);
244     path.cubicTo(201, 254, 214, 225, 214, 225);
245     path.close();
246     path.moveTo(682, -106);
247     path.lineTo(832, 12);
248     path.lineTo(813, 33);
249     path.lineTo(772, 0);
250     path.cubicTo(716, 29, 668, 76, 628, 140);
251     path.lineTo(527, 44);
252     path.cubicTo(575, -26, 628, -77, 682, -106);
253     path.close();
254     path.moveTo(450, 59);
255     path.lineTo(480, 59);
256     path.lineTo(480, 678);
257     path.lineTo(450, 678);
258     path.cubicTo(450, 678, 450, 59, 450, 59);
259     path.close();
260     path.moveTo(463, 374);
261     path.lineTo(633, 459);
262     path.lineTo(633, 490);
263     path.lineTo(463, 406);
264     path.cubicTo(463, 406, 463, 374, 463, 374);
265     path.close();
266     path.moveTo(463, 269);
267     path.lineTo(667, 372);
268     path.lineTo(667, 403);
269     path.lineTo(463, 301);
270     path.cubicTo(463, 301, 463, 269, 463, 269);
271     path.close();
272 
273     SkPath path2;
274     path2.setFillType(SkPathFillType::kWinding);
275     path2.moveTo(492.041f, 525.339f);
276     path2.cubicTo(492.041f, 521.624f, 490.727f, 518.453f, 488.1f, 515.826f);
277     path2.cubicTo(485.473f, 513.199f, 482.302f, 511.886f, 478.587f, 511.886f);
278     path2.cubicTo(474.872f, 511.886f, 471.701f, 513.199f, 469.074f, 515.826f);
279     path2.cubicTo(466.447f, 518.453f, 465.134f, 521.624f, 465.134f, 525.339f);
280     path2.cubicTo(465.134f, 529.054f, 466.447f, 532.226f, 469.074f, 534.853f);
281     path2.cubicTo(471.701f, 537.479f, 474.872f, 538.793f, 478.587f, 538.793f);
282     path2.cubicTo(482.302f, 538.793f, 485.473f, 537.479f, 488.1f, 534.853f);
283     path2.cubicTo(490.727f, 532.226f, 492.041f, 529.054f, 492.041f, 525.339f);
284     path2.close();
285 
286     SkPath opResult;
287     Op(path, path2, kDifference_SkPathOp, &opResult);
288 
289     SkPath winding;
290     path2.setFillType(SkPathFillType::kWinding);
291     REPORTER_ASSERT(reporter, AsWinding(opResult, &winding));
292     REPORTER_ASSERT(reporter, winding.getFillType() == SkPathFillType::kWinding);
293 
294     SkPath difference;
295     Op(winding, opResult, kXOR_SkPathOp, &difference);
296     REPORTER_ASSERT(reporter, difference.isEmpty());
297 }
298 
bug12040_4(skiatest::Reporter * reporter)299 void bug12040_4(skiatest::Reporter* reporter) {
300     for (int moveX = 199; moveX <= 201; ++moveX) {
301         for (int moveY = 299; moveY <= 301; ++moveY) {
302             for (int lineX = 199; lineX <= 201; ++lineX) {
303                 for (int lineY = 199; lineY <= 201; ++lineY) {
304                     SkPath path;
305                     path.setFillType(SkPathFillType::kWinding);
306                     path.addCircle(250, 250, 150);
307 
308                     SkPath path2;
309                     path2.setFillType(SkPathFillType::kWinding);
310                     path2.moveTo(moveX, moveY);  // 200, 300 works... But not 200, 301!!
311                     path2.lineTo(lineX, lineY);  // 200, 200 works... But not 199, 200!!
312                     path2.lineTo(300, 300);
313                     path2.close();
314 
315                     SkPath opResult;
316                     Op(path, path2, kDifference_SkPathOp, &opResult);
317 
318                     SkPath winding;
319                     REPORTER_ASSERT(reporter, AsWinding(opResult, &winding));
320                     REPORTER_ASSERT(reporter, winding.getFillType() == SkPathFillType::kWinding);
321 
322                     SkPath difference;
323                     Op(winding, opResult, kXOR_SkPathOp, &difference);
324                     REPORTER_ASSERT(reporter, difference.isEmpty());
325                 }
326             }
327         }
328     }
329 }
330 
bug12040_5(skiatest::Reporter * reporter)331 void bug12040_5(skiatest::Reporter* reporter) {
332     for (int moveX = 199; moveX <= 201; ++moveX) {
333         for (int moveY = 299; moveY <= 301; ++moveY) {
334             for (int lineX = 199; lineX <= 201; ++lineX) {
335                 for (int lineY = 199; lineY <= 201; ++lineY) {
336                     SkPath path;
337                     path.setFillType(SkPathFillType::kWinding);
338                     path.addRect(100, 100, 400, 400);
339 
340                     SkPath path2;
341                     path2.setFillType(SkPathFillType::kWinding);
342                     path2.moveTo(moveX, moveY);
343                     path2.lineTo(lineX, lineY);  // 200, 200 works... But not 199, 200!!
344                     path2.lineTo(300, 300);
345                     path2.close();
346 
347                     SkPath opResult;
348                     Op(path, path2, kDifference_SkPathOp, &opResult);
349 
350                     SkPath winding;
351                     REPORTER_ASSERT(reporter, AsWinding(opResult, &winding));
352                     REPORTER_ASSERT(reporter, winding.getFillType() == SkPathFillType::kWinding);
353 
354                     SkPath difference;
355                     Op(winding, opResult, kXOR_SkPathOp, &difference);
356                     REPORTER_ASSERT(reporter, difference.isEmpty());
357                 }
358             }
359         }
360     }
361 }
362 
bug13496_1(skiatest::Reporter * reporter)363 void bug13496_1(skiatest::Reporter* reporter) {
364     std::string originalPathStr =
365             "M5.93 -3.12C5.93 -5.03 4.73 -6.06 3.5 -6.06C2.67 -6.06 1.98 -5.59 1.76 -5.34L1.67 "
366             "-5.93L0.75 -5.93L0.75 2.23L1.87 2.04L1.87 -0.12C2.12 -0.03 2.62 0.07 3.18 0.07C4.57 "
367             "0.07 5.93 -1.06 5.93 -3.12ZM4.81 -3.09C4.81 -1.51 4.18 -0.85 3.17 -0.85C2.57 -0.85 "
368             "2.15 -0.98 1.87 -1.12L1.87 -4.15C2.34 -4.73 2.75 -5.09 3.42 -5.09C4.31 -5.09 4.81 "
369             "-4.46 4.81 -3.09Z";
370 
371     SkPath path;
372     SkParsePath::FromSVGString(originalPathStr.c_str(), &path);
373 
374     SkPath simplifiedPath;
375     Simplify(path, &simplifiedPath);
376 
377     SkPath windingPath;
378     REPORTER_ASSERT(reporter, AsWinding(simplifiedPath, &windingPath));
379     REPORTER_ASSERT(reporter, windingPath.getFillType() == SkPathFillType::kWinding);
380 
381     SkPath difference;
382     Op(windingPath, simplifiedPath, kXOR_SkPathOp, &difference);
383     REPORTER_ASSERT(reporter, difference.isEmpty());
384 }
385 
bug13496_2(skiatest::Reporter * reporter)386 void bug13496_2(skiatest::Reporter* reporter) {
387     std::string originalPathStr =
388             "M4 0"
389             "L0 0"
390             "L0 5"
391             "L4 4"
392             "Z"
393             "M3 3"
394             "L1 3"
395             "L1 1"
396             "L3 1"
397             "Z";
398 
399     SkPath path;
400     SkParsePath::FromSVGString(originalPathStr.c_str(), &path);
401 
402     SkPath simplifiedPath;
403     Simplify(path, &simplifiedPath);
404 
405     SkPath windingPath;
406     REPORTER_ASSERT(reporter, AsWinding(simplifiedPath, &windingPath));
407     REPORTER_ASSERT(reporter, windingPath.getFillType() == SkPathFillType::kWinding);
408 
409     SkPath difference;
410     Op(windingPath, simplifiedPath, kXOR_SkPathOp, &difference);
411     REPORTER_ASSERT(reporter, difference.isEmpty());
412 }
413 
bug13496_3(skiatest::Reporter * reporter)414 void bug13496_3(skiatest::Reporter* reporter) {
415     std::string originalPathStr =
416             "M4 0"
417             "L0 0"
418             "L0 4"
419             "L4 4"
420             "Z"
421             "M3 3"
422             "L1 3"
423             "L1 1"
424             "L3 1"
425             "Z";
426 
427     SkPath path;
428     SkParsePath::FromSVGString(originalPathStr.c_str(), &path);
429 
430     SkPath simplifiedPath;
431     Simplify(path, &simplifiedPath);
432 
433     SkPath windingPath;
434     REPORTER_ASSERT(reporter, AsWinding(simplifiedPath, &windingPath));
435     REPORTER_ASSERT(reporter, windingPath.getFillType() == SkPathFillType::kWinding);
436 
437     SkPath difference;
438     Op(windingPath, simplifiedPath, kXOR_SkPathOp, &difference);
439     REPORTER_ASSERT(reporter, difference.isEmpty());
440 }
441 
DEF_TEST(PathOpsAsWinding,reporter)442 DEF_TEST(PathOpsAsWinding, reporter) {
443     SkPath test, result;
444     test.addRect({1, 2, 3, 4});
445     // if test is winding
446     REPORTER_ASSERT(reporter, AsWinding(test, &result));
447     REPORTER_ASSERT(reporter, test == result);
448     // if test is empty
449     test.reset();
450     test.setFillType(SkPathFillType::kEvenOdd);
451     REPORTER_ASSERT(reporter, AsWinding(test, &result));
452     REPORTER_ASSERT(reporter, result.isEmpty());
453     REPORTER_ASSERT(reporter, result.getFillType() == SkPathFillType::kWinding);
454     // if test is convex
455     test.addCircle(5, 5, 10);
456     REPORTER_ASSERT(reporter, AsWinding(test, &result));
457     REPORTER_ASSERT(reporter, result.isConvex());
458     test.setFillType(SkPathFillType::kWinding);
459     REPORTER_ASSERT(reporter, test == result);
460     // if test has infinity
461     test.reset();
462     test.addRect({1, 2, 3, SK_ScalarInfinity});
463     test.setFillType(SkPathFillType::kEvenOdd);
464     REPORTER_ASSERT(reporter, !AsWinding(test, &result));
465     // if test has only one contour
466     test.reset();
467     SkPoint ell[] = {{0, 0}, {4, 0}, {4, 1}, {1, 1}, {1, 4}, {0, 4}};
468     test.addPoly(ell, std::size(ell), true);
469     test.setFillType(SkPathFillType::kEvenOdd);
470     REPORTER_ASSERT(reporter, AsWinding(test, &result));
471     REPORTER_ASSERT(reporter, !result.isConvex());
472     test.setFillType(SkPathFillType::kWinding);
473     REPORTER_ASSERT(reporter, test == result);
474     // test two contours that do not overlap or share bounds
475     test.addRect({5, 2, 6, 3});
476     test.setFillType(SkPathFillType::kEvenOdd);
477     REPORTER_ASSERT(reporter, AsWinding(test, &result));
478     REPORTER_ASSERT(reporter, !result.isConvex());
479     test.setFillType(SkPathFillType::kWinding);
480     REPORTER_ASSERT(reporter, test == result);
481     // test two contours that do not overlap but share bounds
482     test.reset();
483     test.addPoly(ell, std::size(ell), true);
484     test.addRect({2, 2, 3, 3});
485     test.setFillType(SkPathFillType::kEvenOdd);
486     REPORTER_ASSERT(reporter, AsWinding(test, &result));
487     REPORTER_ASSERT(reporter, !result.isConvex());
488     test.setFillType(SkPathFillType::kWinding);
489     REPORTER_ASSERT(reporter, test == result);
490     // test two contours that partially overlap
491     test.reset();
492     test.addRect({0, 0, 3, 3});
493     test.addRect({1, 1, 4, 4});
494     test.setFillType(SkPathFillType::kEvenOdd);
495     REPORTER_ASSERT(reporter, AsWinding(test, &result));
496     REPORTER_ASSERT(reporter, !result.isConvex());
497     test.setFillType(SkPathFillType::kWinding);
498     REPORTER_ASSERT(reporter, test == result);
499     // test that result may be input
500     SkPath copy = test;
501     test.setFillType(SkPathFillType::kEvenOdd);
502     REPORTER_ASSERT(reporter, AsWinding(test, &test));
503     REPORTER_ASSERT(reporter, !test.isConvex());
504     REPORTER_ASSERT(reporter, test == copy);
505     // test a in b, b in a, cw/ccw
506     constexpr SkRect rectA = {0, 0, 3, 3};
507     constexpr SkRect rectB = {1, 1, 2, 2};
508     const std::initializer_list<SkPoint> revBccw = {{1, 2}, {2, 2}, {2, 1}, {1, 1}};
509     const std::initializer_list<SkPoint> revBcw  = {{2, 1}, {2, 2}, {1, 2}, {1, 1}};
510     for (bool aFirst : {false, true}) {
511         for (auto dirA : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
512             for (auto dirB : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
513                 test.reset();
514                 test.setFillType(SkPathFillType::kEvenOdd);
515                 if (aFirst) {
516                     test.addRect(rectA, dirA);
517                     test.addRect(rectB, dirB);
518                 } else {
519                     test.addRect(rectB, dirB);
520                     test.addRect(rectA, dirA);
521                 }
522                 SkPath original = test;
523                 REPORTER_ASSERT(reporter, AsWinding(test, &result));
524                 REPORTER_ASSERT(reporter, result.getFillType() == SkPathFillType::kWinding);
525                 test.reset();
526                 if (aFirst) {
527                     test.addRect(rectA, dirA);
528                 }
529                 if (dirA != dirB) {
530                     test.addRect(rectB, dirB);
531                 } else {
532                     test.addPoly(SkPathDirection::kCW == dirA ? revBccw : revBcw, true);
533                 }
534                 if (!aFirst) {
535                     test.addRect(rectA, dirA);
536                 }
537                 REPORTER_ASSERT(reporter, test == result);
538                 // test that result may be input
539                 REPORTER_ASSERT(reporter, AsWinding(original, &original));
540                 REPORTER_ASSERT(reporter, original.getFillType() == SkPathFillType::kWinding);
541                 REPORTER_ASSERT(reporter, original == result);
542             }
543         }
544     }
545     // Test curve types with donuts. Create a donut with outer and hole in all directions.
546     // After converting to winding, all donuts should have a hole in the middle.
547     for (bool aFirst : {false, true}) {
548         for (auto dirA : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
549             for (auto dirB : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
550                 for (auto curveA : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
551                                      SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
552                     SkPath pathA = build_squircle(curveA, rectA, dirA);
553                     for (auto curveB : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
554                                      SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
555                         test = aFirst ? pathA : SkPath();
556                         test.addPath(build_squircle(curveB, rectB, dirB));
557                         if (!aFirst) {
558                             test.addPath(pathA);
559                         }
560                         test.setFillType(SkPathFillType::kEvenOdd);
561                         REPORTER_ASSERT(reporter, AsWinding(test, &result));
562                        REPORTER_ASSERT(reporter, result.getFillType() == SkPathFillType::kWinding);
563                         for (SkScalar x = rectA.fLeft - 1; x <= rectA.fRight + 1; ++x) {
564                             for (SkScalar y = rectA.fTop - 1; y <= rectA.fBottom + 1; ++y) {
565                                 bool evenOddContains = test.contains(x, y);
566                                 bool windingContains = result.contains(x, y);
567                                 REPORTER_ASSERT(reporter, evenOddContains == windingContains);
568                             }
569                         }
570                     }
571                 }
572             }
573         }
574     }
575     // test https://bugs.chromium.org/p/skia/issues/detail?id=12040
576     bug12040_1(reporter);
577     bug12040_2(reporter);
578     bug12040_3(reporter);
579     bug12040_4(reporter);
580     bug12040_5(reporter);
581     // test https://bugs.chromium.org/p/skia/issues/detail?id=13496
582     bug13496_1(reporter);
583     bug13496_2(reporter);
584     bug13496_3(reporter);
585 }
586