skia2/tests/PathOpsAsWindingTest.cpp
Mike Reed 30bc527fd6 Revert "Reland "Revert "Use flat version of path-direction enum"""
This reverts commit 7cda6f3743.

Reason for revert: legacy convexity code removed -- google3 should be ok

Original change's description:
> Reland "Revert "Use flat version of path-direction enum""
>
> This reverts commit 1792b19485.
>
> Reason for revert: need to update legacy_convexity, still used by google3
>
> Original change's description:
> > Revert "Revert "Use flat version of path-direction enum""
> >
> > This reverts commit 0dacc6b7d3.
> >
> > Change-Id: Ie103e9f36b07e4ee256a3688a4decf3a6dd74314
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/255832
> > Auto-Submit: Mike Reed <reed@google.com>
> > Reviewed-by: Mike Reed <reed@google.com>
> > Commit-Queue: Mike Reed <reed@google.com>
>
> TBR=reed@google.com
>
> Change-Id: I0ecea0eb8a237298c6b908cc4bfd1cacdfc5b900
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/255976
> Reviewed-by: Mike Reed <reed@google.com>
> Commit-Queue: Mike Reed <reed@google.com>

TBR=reed@google.com

Change-Id: I3529139bb391c4f62ce5cd0744cdf38a13b71d78
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/255984
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Mike Reed <reed@google.com>
2019-11-22 19:09:54 +00:00

187 lines
8.3 KiB
C++

/*
* 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, SkPathDirection dir) {
SkPath path;
bool reverse = SkPathDirection::kCCW == 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 : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
for (auto dirB : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
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(SkPathDirection::kCW == 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 : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
for (auto dirB : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
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);
}
}
}
}
}
}
}
}