blob: f122678f7dccee3baf1194d0f6ec7cd8c7eef916 [file] [log] [blame]
Cary Clark7d06e262018-08-16 11:53:54 -04001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "tests/PathOpsExtendedTest.h"
9#include "tests/PathOpsThreadedCommon.h"
10#include "tests/Test.h"
Cary Clark7d06e262018-08-16 11:53:54 -040011
Mike Reed30bc5272019-11-22 18:34:02 +000012static SkPath build_squircle(SkPath::Verb verb, const SkRect& rect, SkPathDirection dir) {
Cary Clark7d06e262018-08-16 11:53:54 -040013 SkPath path;
Mike Reed30bc5272019-11-22 18:34:02 +000014 bool reverse = SkPathDirection::kCCW == dir;
Cary Clark7d06e262018-08-16 11:53:54 -040015 switch (verb) {
16 case SkPath::kLine_Verb:
17 path.addRect(rect, dir);
18 reverse = false;
19 break;
20 case SkPath::kQuad_Verb:
21 path.moveTo(rect.centerX(), rect.fTop);
22 path.quadTo(rect.fRight, rect.fTop, rect.fRight, rect.centerY());
23 path.quadTo(rect.fRight, rect.fBottom, rect.centerX(), rect.fBottom);
24 path.quadTo(rect.fLeft, rect.fBottom, rect.fLeft, rect.centerY());
25 path.quadTo(rect.fLeft, rect.fTop, rect.centerX(), rect.fTop);
26 break;
27 case SkPath::kConic_Verb:
28 path.addCircle(rect.centerX(), rect.centerY(), rect.width() / 2, dir);
29 reverse = false;
30 break;
31 case SkPath::kCubic_Verb: {
32 SkScalar aX14 = rect.fLeft + rect.width() * 1 / 4;
33 SkScalar aX34 = rect.fLeft + rect.width() * 3 / 4;
34 SkScalar aY14 = rect.fTop + rect.height() * 1 / 4;
35 SkScalar aY34 = rect.fTop + rect.height() * 3 / 4;
36 path.moveTo(rect.centerX(), rect.fTop);
37 path.cubicTo(aX34, rect.fTop, rect.fRight, aY14, rect.fRight, rect.centerY());
38 path.cubicTo(rect.fRight, aY34, aX34, rect.fBottom, rect.centerX(), rect.fBottom);
39 path.cubicTo(aX14, rect.fBottom, rect.fLeft, aY34, rect.fLeft, rect.centerY());
40 path.cubicTo(rect.fLeft, aY14, aX14, rect.fTop, rect.centerX(), rect.fTop);
41 } break;
42 default:
43 SkASSERT(0);
44 }
45 if (reverse) {
46 SkPath temp;
47 temp.reverseAddPath(path);
48 path.swap(temp);
49 }
50 return path;
51}
52
53DEF_TEST(PathOpsAsWinding, reporter) {
54 SkPath test, result;
55 test.addRect({1, 2, 3, 4});
56 // if test is winding
57 REPORTER_ASSERT(reporter, AsWinding(test, &result));
58 REPORTER_ASSERT(reporter, test == result);
59 // if test is empty
60 test.reset();
Mike Reed7d34dc72019-11-26 12:17:17 -050061 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -040062 REPORTER_ASSERT(reporter, AsWinding(test, &result));
63 REPORTER_ASSERT(reporter, result.isEmpty());
Mike Reedcf0e3c62019-12-03 16:26:15 -050064 REPORTER_ASSERT(reporter, result.getFillType() == SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -040065 // if test is convex
66 test.addCircle(5, 5, 10);
67 REPORTER_ASSERT(reporter, AsWinding(test, &result));
68 REPORTER_ASSERT(reporter, result.isConvex());
Mike Reed7d34dc72019-11-26 12:17:17 -050069 test.setFillType(SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -040070 REPORTER_ASSERT(reporter, test == result);
71 // if test has infinity
72 test.reset();
73 test.addRect({1, 2, 3, SK_ScalarInfinity});
Mike Reed7d34dc72019-11-26 12:17:17 -050074 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -040075 REPORTER_ASSERT(reporter, !AsWinding(test, &result));
76 // if test has only one contour
77 test.reset();
78 SkPoint ell[] = {{0, 0}, {4, 0}, {4, 1}, {1, 1}, {1, 4}, {0, 4}};
79 test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
Mike Reed7d34dc72019-11-26 12:17:17 -050080 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -040081 REPORTER_ASSERT(reporter, AsWinding(test, &result));
82 REPORTER_ASSERT(reporter, !result.isConvex());
Mike Reed7d34dc72019-11-26 12:17:17 -050083 test.setFillType(SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -040084 REPORTER_ASSERT(reporter, test == result);
85 // test two contours that do not overlap or share bounds
86 test.addRect({5, 2, 6, 3});
Mike Reed7d34dc72019-11-26 12:17:17 -050087 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -040088 REPORTER_ASSERT(reporter, AsWinding(test, &result));
89 REPORTER_ASSERT(reporter, !result.isConvex());
Mike Reed7d34dc72019-11-26 12:17:17 -050090 test.setFillType(SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -040091 REPORTER_ASSERT(reporter, test == result);
92 // test two contours that do not overlap but share bounds
93 test.reset();
94 test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
95 test.addRect({2, 2, 3, 3});
Mike Reed7d34dc72019-11-26 12:17:17 -050096 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -040097 REPORTER_ASSERT(reporter, AsWinding(test, &result));
98 REPORTER_ASSERT(reporter, !result.isConvex());
Mike Reed7d34dc72019-11-26 12:17:17 -050099 test.setFillType(SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -0400100 REPORTER_ASSERT(reporter, test == result);
101 // test two contours that partially overlap
102 test.reset();
103 test.addRect({0, 0, 3, 3});
104 test.addRect({1, 1, 4, 4});
Mike Reed7d34dc72019-11-26 12:17:17 -0500105 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -0400106 REPORTER_ASSERT(reporter, AsWinding(test, &result));
107 REPORTER_ASSERT(reporter, !result.isConvex());
Mike Reed7d34dc72019-11-26 12:17:17 -0500108 test.setFillType(SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -0400109 REPORTER_ASSERT(reporter, test == result);
110 // test that result may be input
111 SkPath copy = test;
Mike Reed7d34dc72019-11-26 12:17:17 -0500112 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -0400113 REPORTER_ASSERT(reporter, AsWinding(test, &test));
114 REPORTER_ASSERT(reporter, !test.isConvex());
115 REPORTER_ASSERT(reporter, test == copy);
116 // test a in b, b in a, cw/ccw
117 constexpr SkRect rectA = {0, 0, 3, 3};
118 constexpr SkRect rectB = {1, 1, 2, 2};
119 const std::initializer_list<SkPoint> revBccw = {{1, 2}, {2, 2}, {2, 1}, {1, 1}};
120 const std::initializer_list<SkPoint> revBcw = {{2, 1}, {2, 2}, {1, 2}, {1, 1}};
121 for (bool aFirst : {false, true}) {
Mike Reed30bc5272019-11-22 18:34:02 +0000122 for (auto dirA : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
123 for (auto dirB : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
Cary Clark7d06e262018-08-16 11:53:54 -0400124 test.reset();
Mike Reed7d34dc72019-11-26 12:17:17 -0500125 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -0400126 if (aFirst) {
127 test.addRect(rectA, dirA);
128 test.addRect(rectB, dirB);
129 } else {
130 test.addRect(rectB, dirB);
131 test.addRect(rectA, dirA);
132 }
133 SkPath original = test;
134 REPORTER_ASSERT(reporter, AsWinding(test, &result));
Mike Reedcf0e3c62019-12-03 16:26:15 -0500135 REPORTER_ASSERT(reporter, result.getFillType() == SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -0400136 test.reset();
137 if (aFirst) {
138 test.addRect(rectA, dirA);
139 }
140 if (dirA != dirB) {
141 test.addRect(rectB, dirB);
142 } else {
Mike Reed30bc5272019-11-22 18:34:02 +0000143 test.addPoly(SkPathDirection::kCW == dirA ? revBccw : revBcw, true);
Cary Clark7d06e262018-08-16 11:53:54 -0400144 }
145 if (!aFirst) {
146 test.addRect(rectA, dirA);
147 }
148 REPORTER_ASSERT(reporter, test == result);
149 // test that result may be input
150 REPORTER_ASSERT(reporter, AsWinding(original, &original));
Mike Reedcf0e3c62019-12-03 16:26:15 -0500151 REPORTER_ASSERT(reporter, original.getFillType() == SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -0400152 REPORTER_ASSERT(reporter, original == result);
153 }
154 }
155 }
156 // Test curve types with donuts. Create a donut with outer and hole in all directions.
157 // After converting to winding, all donuts should have a hole in the middle.
158 for (bool aFirst : {false, true}) {
Mike Reed30bc5272019-11-22 18:34:02 +0000159 for (auto dirA : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
160 for (auto dirB : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
Cary Clark7d06e262018-08-16 11:53:54 -0400161 for (auto curveA : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
162 SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
163 SkPath pathA = build_squircle(curveA, rectA, dirA);
164 for (auto curveB : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
165 SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
166 test = aFirst ? pathA : SkPath();
167 test.addPath(build_squircle(curveB, rectB, dirB));
168 if (!aFirst) {
169 test.addPath(pathA);
170 }
Mike Reed7d34dc72019-11-26 12:17:17 -0500171 test.setFillType(SkPathFillType::kEvenOdd);
Cary Clark7d06e262018-08-16 11:53:54 -0400172 REPORTER_ASSERT(reporter, AsWinding(test, &result));
Mike Reedcf0e3c62019-12-03 16:26:15 -0500173 REPORTER_ASSERT(reporter, result.getFillType() == SkPathFillType::kWinding);
Cary Clark7d06e262018-08-16 11:53:54 -0400174 for (SkScalar x = rectA.fLeft - 1; x <= rectA.fRight + 1; ++x) {
175 for (SkScalar y = rectA.fTop - 1; y <= rectA.fBottom + 1; ++y) {
176 bool evenOddContains = test.contains(x, y);
177 bool windingContains = result.contains(x, y);
178 REPORTER_ASSERT(reporter, evenOddContains == windingContains);
179 }
180 }
181 }
182 }
183 }
184 }
185 }
186}