123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- /*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "tests/PathOpsExtendedTest.h"
- #include "tests/PathOpsThreadedCommon.h"
- #include "tests/Test.h"
- static SkPath build_squircle(SkPath::Verb verb, const SkRect& rect, SkPath::Direction dir) {
- SkPath path;
- bool reverse = SkPath::kCCW_Direction == dir;
- switch (verb) {
- case SkPath::kLine_Verb:
- path.addRect(rect, dir);
- reverse = false;
- break;
- case SkPath::kQuad_Verb:
- path.moveTo(rect.centerX(), rect.fTop);
- path.quadTo(rect.fRight, rect.fTop, rect.fRight, rect.centerY());
- path.quadTo(rect.fRight, rect.fBottom, rect.centerX(), rect.fBottom);
- path.quadTo(rect.fLeft, rect.fBottom, rect.fLeft, rect.centerY());
- path.quadTo(rect.fLeft, rect.fTop, rect.centerX(), rect.fTop);
- break;
- case SkPath::kConic_Verb:
- path.addCircle(rect.centerX(), rect.centerY(), rect.width() / 2, dir);
- reverse = false;
- break;
- case SkPath::kCubic_Verb: {
- SkScalar aX14 = rect.fLeft + rect.width() * 1 / 4;
- SkScalar aX34 = rect.fLeft + rect.width() * 3 / 4;
- SkScalar aY14 = rect.fTop + rect.height() * 1 / 4;
- SkScalar aY34 = rect.fTop + rect.height() * 3 / 4;
- path.moveTo(rect.centerX(), rect.fTop);
- path.cubicTo(aX34, rect.fTop, rect.fRight, aY14, rect.fRight, rect.centerY());
- path.cubicTo(rect.fRight, aY34, aX34, rect.fBottom, rect.centerX(), rect.fBottom);
- path.cubicTo(aX14, rect.fBottom, rect.fLeft, aY34, rect.fLeft, rect.centerY());
- path.cubicTo(rect.fLeft, aY14, aX14, rect.fTop, rect.centerX(), rect.fTop);
- } break;
- default:
- SkASSERT(0);
- }
- if (reverse) {
- SkPath temp;
- temp.reverseAddPath(path);
- path.swap(temp);
- }
- return path;
- }
- DEF_TEST(PathOpsAsWinding, reporter) {
- SkPath test, result;
- test.addRect({1, 2, 3, 4});
- // if test is winding
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, test == result);
- // if test is empty
- test.reset();
- test.setFillType(SkPath::kEvenOdd_FillType);
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, result.isEmpty());
- REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
- // if test is convex
- test.addCircle(5, 5, 10);
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, result.isConvex());
- test.setFillType(SkPath::kWinding_FillType);
- REPORTER_ASSERT(reporter, test == result);
- // if test has infinity
- test.reset();
- test.addRect({1, 2, 3, SK_ScalarInfinity});
- test.setFillType(SkPath::kEvenOdd_FillType);
- REPORTER_ASSERT(reporter, !AsWinding(test, &result));
- // if test has only one contour
- test.reset();
- SkPoint ell[] = {{0, 0}, {4, 0}, {4, 1}, {1, 1}, {1, 4}, {0, 4}};
- test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
- test.setFillType(SkPath::kEvenOdd_FillType);
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, !result.isConvex());
- test.setFillType(SkPath::kWinding_FillType);
- REPORTER_ASSERT(reporter, test == result);
- // test two contours that do not overlap or share bounds
- test.addRect({5, 2, 6, 3});
- test.setFillType(SkPath::kEvenOdd_FillType);
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, !result.isConvex());
- test.setFillType(SkPath::kWinding_FillType);
- REPORTER_ASSERT(reporter, test == result);
- // test two contours that do not overlap but share bounds
- test.reset();
- test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
- test.addRect({2, 2, 3, 3});
- test.setFillType(SkPath::kEvenOdd_FillType);
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, !result.isConvex());
- test.setFillType(SkPath::kWinding_FillType);
- REPORTER_ASSERT(reporter, test == result);
- // test two contours that partially overlap
- test.reset();
- test.addRect({0, 0, 3, 3});
- test.addRect({1, 1, 4, 4});
- test.setFillType(SkPath::kEvenOdd_FillType);
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, !result.isConvex());
- test.setFillType(SkPath::kWinding_FillType);
- REPORTER_ASSERT(reporter, test == result);
- // test that result may be input
- SkPath copy = test;
- test.setFillType(SkPath::kEvenOdd_FillType);
- REPORTER_ASSERT(reporter, AsWinding(test, &test));
- REPORTER_ASSERT(reporter, !test.isConvex());
- REPORTER_ASSERT(reporter, test == copy);
- // test a in b, b in a, cw/ccw
- constexpr SkRect rectA = {0, 0, 3, 3};
- constexpr SkRect rectB = {1, 1, 2, 2};
- const std::initializer_list<SkPoint> revBccw = {{1, 2}, {2, 2}, {2, 1}, {1, 1}};
- const std::initializer_list<SkPoint> revBcw = {{2, 1}, {2, 2}, {1, 2}, {1, 1}};
- for (bool aFirst : {false, true}) {
- for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
- for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
- test.reset();
- test.setFillType(SkPath::kEvenOdd_FillType);
- if (aFirst) {
- test.addRect(rectA, dirA);
- test.addRect(rectB, dirB);
- } else {
- test.addRect(rectB, dirB);
- test.addRect(rectA, dirA);
- }
- SkPath original = test;
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
- test.reset();
- if (aFirst) {
- test.addRect(rectA, dirA);
- }
- if (dirA != dirB) {
- test.addRect(rectB, dirB);
- } else {
- test.addPoly(SkPath::kCW_Direction == dirA ? revBccw : revBcw, true);
- }
- if (!aFirst) {
- test.addRect(rectA, dirA);
- }
- REPORTER_ASSERT(reporter, test == result);
- // test that result may be input
- REPORTER_ASSERT(reporter, AsWinding(original, &original));
- REPORTER_ASSERT(reporter, original.getFillType() == SkPath::kWinding_FillType);
- REPORTER_ASSERT(reporter, original == result);
- }
- }
- }
- // Test curve types with donuts. Create a donut with outer and hole in all directions.
- // After converting to winding, all donuts should have a hole in the middle.
- for (bool aFirst : {false, true}) {
- for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
- for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
- for (auto curveA : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
- SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
- SkPath pathA = build_squircle(curveA, rectA, dirA);
- for (auto curveB : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
- SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
- test = aFirst ? pathA : SkPath();
- test.addPath(build_squircle(curveB, rectB, dirB));
- if (!aFirst) {
- test.addPath(pathA);
- }
- test.setFillType(SkPath::kEvenOdd_FillType);
- REPORTER_ASSERT(reporter, AsWinding(test, &result));
- REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
- for (SkScalar x = rectA.fLeft - 1; x <= rectA.fRight + 1; ++x) {
- for (SkScalar y = rectA.fTop - 1; y <= rectA.fBottom + 1; ++y) {
- bool evenOddContains = test.contains(x, y);
- bool windingContains = result.contains(x, y);
- REPORTER_ASSERT(reporter, evenOddContains == windingContains);
- }
- }
- }
- }
- }
- }
- }
- }
|