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 "PathOpsExtendedTest.h" 9 #include "PathOpsThreadedCommon.h" 10 #include "Test.h" 11 12 static SkPath build_squircle(SkPath::Verb verb, const SkRect& rect, SkPath::Direction dir) { 13 SkPath path; 14 bool reverse = SkPath::kCCW_Direction == dir; 15 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 53 DEF_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(); 61 test.setFillType(SkPath::kEvenOdd_FillType); 62 REPORTER_ASSERT(reporter, AsWinding(test, &result)); 63 REPORTER_ASSERT(reporter, result.isEmpty()); 64 REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType); 65 // if test is convex 66 test.addCircle(5, 5, 10); 67 REPORTER_ASSERT(reporter, AsWinding(test, &result)); 68 REPORTER_ASSERT(reporter, result.isConvex()); 69 test.setFillType(SkPath::kWinding_FillType); 70 REPORTER_ASSERT(reporter, test == result); 71 // if test has infinity 72 test.reset(); 73 test.addRect({1, 2, 3, SK_ScalarInfinity}); 74 test.setFillType(SkPath::kEvenOdd_FillType); 75 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); 80 test.setFillType(SkPath::kEvenOdd_FillType); 81 REPORTER_ASSERT(reporter, AsWinding(test, &result)); 82 REPORTER_ASSERT(reporter, !result.isConvex()); 83 test.setFillType(SkPath::kWinding_FillType); 84 REPORTER_ASSERT(reporter, test == result); 85 // test two contours that do not overlap or share bounds 86 test.addRect({5, 2, 6, 3}); 87 test.setFillType(SkPath::kEvenOdd_FillType); 88 REPORTER_ASSERT(reporter, AsWinding(test, &result)); 89 REPORTER_ASSERT(reporter, !result.isConvex()); 90 test.setFillType(SkPath::kWinding_FillType); 91 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}); 96 test.setFillType(SkPath::kEvenOdd_FillType); 97 REPORTER_ASSERT(reporter, AsWinding(test, &result)); 98 REPORTER_ASSERT(reporter, !result.isConvex()); 99 test.setFillType(SkPath::kWinding_FillType); 100 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}); 105 test.setFillType(SkPath::kEvenOdd_FillType); 106 REPORTER_ASSERT(reporter, AsWinding(test, &result)); 107 REPORTER_ASSERT(reporter, !result.isConvex()); 108 test.setFillType(SkPath::kWinding_FillType); 109 REPORTER_ASSERT(reporter, test == result); 110 // test that result may be input 111 SkPath copy = test; 112 test.setFillType(SkPath::kEvenOdd_FillType); 113 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}) { 122 for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { 123 for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { 124 test.reset(); 125 test.setFillType(SkPath::kEvenOdd_FillType); 126 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)); 135 REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType); 136 test.reset(); 137 if (aFirst) { 138 test.addRect(rectA, dirA); 139 } 140 if (dirA != dirB) { 141 test.addRect(rectB, dirB); 142 } else { 143 test.addPoly(SkPath::kCW_Direction == dirA ? revBccw : revBcw, true); 144 } 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)); 151 REPORTER_ASSERT(reporter, original.getFillType() == SkPath::kWinding_FillType); 152 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}) { 159 for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { 160 for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { 161 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 } 171 test.setFillType(SkPath::kEvenOdd_FillType); 172 REPORTER_ASSERT(reporter, AsWinding(test, &result)); 173 REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType); 174 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 } 187