PathOpsAsWindingTest.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. #include "tests/PathOpsExtendedTest.h"
  8. #include "tests/PathOpsThreadedCommon.h"
  9. #include "tests/Test.h"
  10. static SkPath build_squircle(SkPath::Verb verb, const SkRect& rect, SkPath::Direction dir) {
  11. SkPath path;
  12. bool reverse = SkPath::kCCW_Direction == dir;
  13. switch (verb) {
  14. case SkPath::kLine_Verb:
  15. path.addRect(rect, dir);
  16. reverse = false;
  17. break;
  18. case SkPath::kQuad_Verb:
  19. path.moveTo(rect.centerX(), rect.fTop);
  20. path.quadTo(rect.fRight, rect.fTop, rect.fRight, rect.centerY());
  21. path.quadTo(rect.fRight, rect.fBottom, rect.centerX(), rect.fBottom);
  22. path.quadTo(rect.fLeft, rect.fBottom, rect.fLeft, rect.centerY());
  23. path.quadTo(rect.fLeft, rect.fTop, rect.centerX(), rect.fTop);
  24. break;
  25. case SkPath::kConic_Verb:
  26. path.addCircle(rect.centerX(), rect.centerY(), rect.width() / 2, dir);
  27. reverse = false;
  28. break;
  29. case SkPath::kCubic_Verb: {
  30. SkScalar aX14 = rect.fLeft + rect.width() * 1 / 4;
  31. SkScalar aX34 = rect.fLeft + rect.width() * 3 / 4;
  32. SkScalar aY14 = rect.fTop + rect.height() * 1 / 4;
  33. SkScalar aY34 = rect.fTop + rect.height() * 3 / 4;
  34. path.moveTo(rect.centerX(), rect.fTop);
  35. path.cubicTo(aX34, rect.fTop, rect.fRight, aY14, rect.fRight, rect.centerY());
  36. path.cubicTo(rect.fRight, aY34, aX34, rect.fBottom, rect.centerX(), rect.fBottom);
  37. path.cubicTo(aX14, rect.fBottom, rect.fLeft, aY34, rect.fLeft, rect.centerY());
  38. path.cubicTo(rect.fLeft, aY14, aX14, rect.fTop, rect.centerX(), rect.fTop);
  39. } break;
  40. default:
  41. SkASSERT(0);
  42. }
  43. if (reverse) {
  44. SkPath temp;
  45. temp.reverseAddPath(path);
  46. path.swap(temp);
  47. }
  48. return path;
  49. }
  50. DEF_TEST(PathOpsAsWinding, reporter) {
  51. SkPath test, result;
  52. test.addRect({1, 2, 3, 4});
  53. // if test is winding
  54. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  55. REPORTER_ASSERT(reporter, test == result);
  56. // if test is empty
  57. test.reset();
  58. test.setFillType(SkPath::kEvenOdd_FillType);
  59. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  60. REPORTER_ASSERT(reporter, result.isEmpty());
  61. REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
  62. // if test is convex
  63. test.addCircle(5, 5, 10);
  64. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  65. REPORTER_ASSERT(reporter, result.isConvex());
  66. test.setFillType(SkPath::kWinding_FillType);
  67. REPORTER_ASSERT(reporter, test == result);
  68. // if test has infinity
  69. test.reset();
  70. test.addRect({1, 2, 3, SK_ScalarInfinity});
  71. test.setFillType(SkPath::kEvenOdd_FillType);
  72. REPORTER_ASSERT(reporter, !AsWinding(test, &result));
  73. // if test has only one contour
  74. test.reset();
  75. SkPoint ell[] = {{0, 0}, {4, 0}, {4, 1}, {1, 1}, {1, 4}, {0, 4}};
  76. test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
  77. test.setFillType(SkPath::kEvenOdd_FillType);
  78. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  79. REPORTER_ASSERT(reporter, !result.isConvex());
  80. test.setFillType(SkPath::kWinding_FillType);
  81. REPORTER_ASSERT(reporter, test == result);
  82. // test two contours that do not overlap or share bounds
  83. test.addRect({5, 2, 6, 3});
  84. test.setFillType(SkPath::kEvenOdd_FillType);
  85. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  86. REPORTER_ASSERT(reporter, !result.isConvex());
  87. test.setFillType(SkPath::kWinding_FillType);
  88. REPORTER_ASSERT(reporter, test == result);
  89. // test two contours that do not overlap but share bounds
  90. test.reset();
  91. test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
  92. test.addRect({2, 2, 3, 3});
  93. test.setFillType(SkPath::kEvenOdd_FillType);
  94. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  95. REPORTER_ASSERT(reporter, !result.isConvex());
  96. test.setFillType(SkPath::kWinding_FillType);
  97. REPORTER_ASSERT(reporter, test == result);
  98. // test two contours that partially overlap
  99. test.reset();
  100. test.addRect({0, 0, 3, 3});
  101. test.addRect({1, 1, 4, 4});
  102. test.setFillType(SkPath::kEvenOdd_FillType);
  103. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  104. REPORTER_ASSERT(reporter, !result.isConvex());
  105. test.setFillType(SkPath::kWinding_FillType);
  106. REPORTER_ASSERT(reporter, test == result);
  107. // test that result may be input
  108. SkPath copy = test;
  109. test.setFillType(SkPath::kEvenOdd_FillType);
  110. REPORTER_ASSERT(reporter, AsWinding(test, &test));
  111. REPORTER_ASSERT(reporter, !test.isConvex());
  112. REPORTER_ASSERT(reporter, test == copy);
  113. // test a in b, b in a, cw/ccw
  114. constexpr SkRect rectA = {0, 0, 3, 3};
  115. constexpr SkRect rectB = {1, 1, 2, 2};
  116. const std::initializer_list<SkPoint> revBccw = {{1, 2}, {2, 2}, {2, 1}, {1, 1}};
  117. const std::initializer_list<SkPoint> revBcw = {{2, 1}, {2, 2}, {1, 2}, {1, 1}};
  118. for (bool aFirst : {false, true}) {
  119. for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
  120. for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
  121. test.reset();
  122. test.setFillType(SkPath::kEvenOdd_FillType);
  123. if (aFirst) {
  124. test.addRect(rectA, dirA);
  125. test.addRect(rectB, dirB);
  126. } else {
  127. test.addRect(rectB, dirB);
  128. test.addRect(rectA, dirA);
  129. }
  130. SkPath original = test;
  131. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  132. REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
  133. test.reset();
  134. if (aFirst) {
  135. test.addRect(rectA, dirA);
  136. }
  137. if (dirA != dirB) {
  138. test.addRect(rectB, dirB);
  139. } else {
  140. test.addPoly(SkPath::kCW_Direction == dirA ? revBccw : revBcw, true);
  141. }
  142. if (!aFirst) {
  143. test.addRect(rectA, dirA);
  144. }
  145. REPORTER_ASSERT(reporter, test == result);
  146. // test that result may be input
  147. REPORTER_ASSERT(reporter, AsWinding(original, &original));
  148. REPORTER_ASSERT(reporter, original.getFillType() == SkPath::kWinding_FillType);
  149. REPORTER_ASSERT(reporter, original == result);
  150. }
  151. }
  152. }
  153. // Test curve types with donuts. Create a donut with outer and hole in all directions.
  154. // After converting to winding, all donuts should have a hole in the middle.
  155. for (bool aFirst : {false, true}) {
  156. for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
  157. for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
  158. for (auto curveA : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
  159. SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
  160. SkPath pathA = build_squircle(curveA, rectA, dirA);
  161. for (auto curveB : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
  162. SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
  163. test = aFirst ? pathA : SkPath();
  164. test.addPath(build_squircle(curveB, rectB, dirB));
  165. if (!aFirst) {
  166. test.addPath(pathA);
  167. }
  168. test.setFillType(SkPath::kEvenOdd_FillType);
  169. REPORTER_ASSERT(reporter, AsWinding(test, &result));
  170. REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
  171. for (SkScalar x = rectA.fLeft - 1; x <= rectA.fRight + 1; ++x) {
  172. for (SkScalar y = rectA.fTop - 1; y <= rectA.fBottom + 1; ++y) {
  173. bool evenOddContains = test.contains(x, y);
  174. bool windingContains = result.contains(x, y);
  175. REPORTER_ASSERT(reporter, evenOddContains == windingContains);
  176. }
  177. }
  178. }
  179. }
  180. }
  181. }
  182. }
  183. }