diff options
-rw-r--r-- | src/gpu/GrShape.h | 62 | ||||
-rw-r--r-- | tests/GrShapeTest.cpp | 27 |
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, |