aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/gpu/GrShape.h62
-rw-r--r--tests/GrShapeTest.cpp27
2 files changed, 84 insertions, 5 deletions
diff --git a/src/gpu/GrShape.h b/src/gpu/GrShape.h
index 6b4e46fb82..139b6ed60b 100644
--- a/src/gpu/GrShape.h
+++ b/src/gpu/GrShape.h
@@ -144,7 +144,7 @@ public:
* information from this shape's style to its geometry. Scale is used when approximating the
* output geometry and typically is computed from the view matrix
*/
- GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) {
+ GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
return GrShape(*this, apply, scale);
}
@@ -220,6 +220,61 @@ public:
void styledBounds(SkRect* bounds) const;
/**
+ * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
+ * convex path is considered to be closed if they styling reflects a fill and not otherwise.
+ * This is because filling closes all contours in the path.
+ */
+ bool knownToBeConvex() const {
+ switch (fType) {
+ case Type::kEmpty:
+ return true;
+ case Type::kRRect:
+ return true;
+ case Type::kPath:
+ // SkPath.isConvex() really means "is this path convex were it to be closed" and
+ // thus doesn't give the correct answer for stroked paths, hence we also check
+ // whether the path is either filled or closed. Convex paths may only have one
+ // contour hence isLastContourClosed() is a sufficient for a convex path.
+ return (this->style().isSimpleFill() || fPath.get()->isLastContourClosed()) &&
+ fPath.get()->isConvex();
+ }
+ return false;
+ }
+
+ /** Is the pre-styled geometry inverse filled? */
+ bool inverseFilled() const {
+ bool ret = false;
+ switch (fType) {
+ case Type::kEmpty:
+ ret = false;
+ break;
+ case Type::kRRect:
+ ret = fRRectIsInverted;
+ break;
+ case Type::kPath:
+ ret = this->fPath.get()->isInverseFillType();
+ break;
+ }
+ // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
+ SkASSERT(!(ret && this->style().isDashed()));
+ return ret;
+ }
+
+ /**
+ * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
+ * because an arbitrary path effect could produce an inverse filled path. In other cases this
+ * can be thought of as "inverseFilledAfterStyling()".
+ */
+ bool mayBeInverseFilledAfterStyling() const {
+ // An arbitrary path effect can produce an arbitrary output path, which may be inverse
+ // filled.
+ if (this->style().hasNonDashPathEffect()) {
+ return true;
+ }
+ return this->inverseFilled();
+ }
+
+ /**
* Is it known that the unstyled geometry has no unclosed contours. This means that it will
* not have any caps if stroked (modulo the effect of any path effect).
*/
@@ -230,7 +285,8 @@ public:
case Type::kRRect:
return true;
case Type::kPath:
- return false;
+ // SkPath doesn't keep track of the closed status of each contour.
+ return SkPathPriv::IsClosedSingleContour(*fPath.get());
}
return false;
}
@@ -258,6 +314,8 @@ public:
*/
int unstyledKeySize() const;
+ bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
+
/**
* Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
* space allocated for the key and that unstyledKeySize() does not return a negative value
diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp
index 7636e2a0d0..1ee9bbc018 100644
--- a/tests/GrShapeTest.cpp
+++ b/tests/GrShapeTest.cpp
@@ -296,7 +296,8 @@ static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrS
bool aHasPE = a.style().hasPathEffect();
bool bHasPE = b.style().hasPathEffect();
bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
-
+ // GrShape will close paths with simple fill style.
+ bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill());
SkPath pathA, pathB;
a.asPath(&pathA);
b.asPath(&pathB);
@@ -327,6 +328,8 @@ static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrS
} else {
SkPath pA = pathA;
SkPath pB = pathB;
+ REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
+ REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
if (ignoreInversenessDifference) {
pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
@@ -342,7 +345,7 @@ static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrS
} else {
REPORTER_ASSERT(r, keyA != keyB);
}
- if (a.style().isSimpleFill() != b.style().isSimpleFill()) {
+ if (allowedClosednessDiff) {
// GrShape will close paths with simple fill style. Make the non-filled path closed
// so that the comparision will succeed. Make sure both are closed before comparing.
pA.close();
@@ -358,14 +361,32 @@ static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrS
}
}
REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
- REPORTER_ASSERT(r, a.knownToBeClosed() == b.knownToBeClosed());
+ REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed());
+ // closedness can affect convexity.
+ REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex());
+ if (a.knownToBeConvex()) {
+ REPORTER_ASSERT(r, pathA.isConvex());
+ }
+ if (b.knownToBeConvex()) {
+ REPORTER_ASSERT(r, pathB.isConvex());
+ }
REPORTER_ASSERT(r, a.bounds() == b.bounds());
REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
SkPoint pts[4];
REPORTER_ASSERT(r, a.asLine(pts) == b.asLine(pts + 2));
+ // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other
+ // doesn't (since the PE can set any fill type on its output path).
+ // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other
+ // then they may disagree about inverseness.
+ if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() &&
+ a.style().isDashed() == b.style().isDashed()) {
+ REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() ==
+ b.mayBeInverseFilledAfterStyling());
+ }
if (a.asLine(pts)) {
REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]);
}
+ REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled());
}
void TestCase::compare(skiatest::Reporter* r, const TestCase& that,