diff options
author | Brian Salomon <bsalomon@google.com> | 2017-08-31 13:27:15 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-08-31 18:14:21 +0000 |
commit | d5a3f7f9673a152928332a38e53a5fc55f590f40 (patch) | |
tree | 044a846a2f3755e5bfa47ea22335c9df61b10d64 | |
parent | 5d303ed44b611708784f7b05cfa35a270c8d0c09 (diff) |
Add a GrShape::Type value for an inverted empty path
Change-Id: Ib34a608db07a2ff1d7bdfbd96867fa5ff0ac9782
Reviewed-on: https://skia-review.googlesource.com/41540
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
-rw-r--r-- | src/gpu/GrShape.cpp | 27 | ||||
-rw-r--r-- | src/gpu/GrShape.h | 18 | ||||
-rw-r--r-- | tests/GrShapeTest.cpp | 110 |
3 files changed, 131 insertions, 24 deletions
diff --git a/src/gpu/GrShape.cpp b/src/gpu/GrShape.cpp index eb07ffc558..221247808b 100644 --- a/src/gpu/GrShape.cpp +++ b/src/gpu/GrShape.cpp @@ -13,6 +13,8 @@ GrShape& GrShape::operator=(const GrShape& that) { switch (fType) { case Type::kEmpty: break; + case Type::kInvertedEmpty: + break; case Type::kRRect: fRRectData = that.fRRectData; break; @@ -36,6 +38,8 @@ SkRect GrShape::bounds() const { switch (fType) { case Type::kEmpty: return kInverted; + case Type::kInvertedEmpty: + return kInverted; case Type::kLine: { SkRect bounds; if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) { @@ -64,9 +68,10 @@ SkRect GrShape::bounds() const { } SkRect GrShape::styledBounds() const { - if (Type::kEmpty == fType && !fStyle.hasNonDashPathEffect()) { + if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) { return SkRect::MakeEmpty(); } + SkRect bounds; fStyle.adjustBounds(&bounds, this->bounds()); return bounds; @@ -122,6 +127,8 @@ int GrShape::unstyledKeySize() const { switch (fType) { case Type::kEmpty: return 1; + case Type::kInvertedEmpty: + return 1; case Type::kRRect: SkASSERT(!fInheritedKey.count()); SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t)); @@ -158,6 +165,9 @@ void GrShape::writeUnstyledKey(uint32_t* key) const { case Type::kEmpty: *key++ = 1; break; + case Type::kInvertedEmpty: + *key++ = 2; + break; case Type::kRRect: fRRectData.fRRect.writeToMemory(key); key += SkRRect::kSizeInMemory / sizeof(uint32_t); @@ -244,6 +254,8 @@ GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) { switch (fType) { case Type::kEmpty: break; + case Type::kInvertedEmpty: + break; case Type::kRRect: fRRectData = that.fRRectData; break; @@ -354,7 +366,9 @@ void GrShape::attemptToSimplifyPath() { bool inverted = this->path().isInverseFillType(); SkPoint pts[2]; if (this->path().isEmpty()) { - this->changeType(Type::kEmpty); + // Dashing ignores inverseness skbug.com/5421. + this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty + : Type::kEmpty); } else if (this->path().isLine(pts)) { this->changeType(Type::kLine); fLineData.fPts[0] = pts[0]; @@ -442,7 +456,8 @@ void GrShape::attemptToSimplifyRRect() { SkASSERT(Type::kRRect == fType); SkASSERT(!fInheritedKey.count()); if (fRRectData.fRRect.isEmpty()) { - fType = Type::kEmpty; + // Dashing ignores the inverseness currently. skbug.com/5421 + fType = fRRectData.fInverted && !fStyle.isDashed() ? Type::kInvertedEmpty : Type::kEmpty; return; } if (!this->style().hasPathEffect()) { @@ -480,8 +495,8 @@ void GrShape::attemptToSimplifyLine() { rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false); fStyle = GrStyle(rec, nullptr); } - if (fStyle.isSimpleFill() && !fLineData.fInverted) { - this->changeType(Type::kEmpty); + if (fStyle.isSimpleFill()) { + this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty); return; } SkPoint* pts = fLineData.fPts; @@ -525,7 +540,7 @@ void GrShape::attemptToSimplifyLine() { fRRectData.fStart = kDefaultRRectStart; if (fRRectData.fRRect.isEmpty()) { // This can happen when r is very small relative to the rect edges. - this->changeType(Type::kEmpty); + this->changeType(inverted ? Type::kInvertedEmpty : Type::kEmpty); return; } fStyle = GrStyle::SimpleFill(); diff --git a/src/gpu/GrShape.h b/src/gpu/GrShape.h index 074278cd86..5226c239cd 100644 --- a/src/gpu/GrShape.h +++ b/src/gpu/GrShape.h @@ -176,6 +176,10 @@ public: case Type::kEmpty: out->reset(); break; + case Type::kInvertedEmpty: + out->reset(); + out->setFillType(kDefaultPathInverseFillType); + break; case Type::kRRect: out->reset(); out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart); @@ -204,9 +208,9 @@ public: /** * Returns whether the geometry is empty. Note that applying the style could produce a - * non-empty shape. + * non-empty shape. It also may have an inverse fill. */ - bool isEmpty() const { return Type::kEmpty == fType; } + bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; } /** * Gets the bounds of the geometry without reflecting the shape's styling. This ignores @@ -229,6 +233,8 @@ public: switch (fType) { case Type::kEmpty: return true; + case Type::kInvertedEmpty: + return true; case Type::kRRect: return true; case Type::kLine: @@ -251,6 +257,9 @@ public: case Type::kEmpty: ret = false; break; + case Type::kInvertedEmpty: + ret = true; + break; case Type::kRRect: ret = fRRectData.fInverted; break; @@ -288,6 +297,8 @@ public: switch (fType) { case Type::kEmpty: return true; + case Type::kInvertedEmpty: + return true; case Type::kRRect: return true; case Type::kLine: @@ -303,6 +314,8 @@ public: switch (fType) { case Type::kEmpty: return 0; + case Type::kInvertedEmpty: + return 0; case Type::kRRect: if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) { return SkPath::kConic_SegmentMask; @@ -336,6 +349,7 @@ public: private: enum class Type { kEmpty, + kInvertedEmpty, kRRect, kLine, kPath, diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp index 3f97288c1c..719e5cd974 100644 --- a/tests/GrShapeTest.cpp +++ b/tests/GrShapeTest.cpp @@ -175,7 +175,7 @@ public: bool fillChangesGeom() const override { // unclosed rects get closed. Lines get turned into empty geometry - return this->isUnclosedRect() || (fPath.isLine(nullptr) && !fPath.isInverseFillType()); + return this->isUnclosedRect() || fPath.isLine(nullptr); } bool strokeIsConvertedToFill() const override { @@ -1203,23 +1203,29 @@ void test_volatile_path(skiatest::Reporter* reporter, const Geo& geo) { void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& geo) { /** - * This path effect returns an empty path. + * This path effect returns an empty path (possibly inverted) */ class EmptyPathEffect : SkPathEffect { public: bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect* cullR) const override { dst->reset(); + if (fInvert) { + dst->toggleInverseFillType(); + } return true; } void computeFastBounds(SkRect* dst, const SkRect& src) const override { dst->setEmpty(); } - static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); } + static sk_sp<SkPathEffect> Make(bool invert) { + return sk_sp<SkPathEffect>(new EmptyPathEffect(invert)); + } Factory getFactory() const override { return nullptr; } void toString(SkString*) const override {} private: - EmptyPathEffect() {} + bool fInvert; + EmptyPathEffect(bool invert) : fInvert(invert) {} }; SkPath emptyPath; @@ -1228,17 +1234,27 @@ void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& make_key(&emptyKey, emptyShape); REPORTER_ASSERT(reporter, emptyShape.isEmpty()); + emptyPath.toggleInverseFillType(); + GrShape invertedEmptyShape(emptyPath); + Key invertedEmptyKey; + make_key(&invertedEmptyKey, invertedEmptyShape); + REPORTER_ASSERT(reporter, invertedEmptyShape.isEmpty()); + + REPORTER_ASSERT(reporter, invertedEmptyKey != emptyKey); + SkPaint pe; - pe.setPathEffect(EmptyPathEffect::Make()); - TestCase geoCase(geo, pe, reporter); - REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey); - REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey); - REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey); - REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty()); - REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty()); + pe.setPathEffect(EmptyPathEffect::Make(false)); + TestCase geoPECase(geo, pe, reporter); + REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == emptyKey); + REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == emptyKey); + REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectThenStrokeKey() == emptyKey); + REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().isEmpty()); + REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().isEmpty()); + REPORTER_ASSERT(reporter, !geoPECase.appliedPathEffectShape().inverseFilled()); + REPORTER_ASSERT(reporter, !geoPECase.appliedFullStyleShape().inverseFilled()); SkPaint peStroke; - peStroke.setPathEffect(EmptyPathEffect::Make()); + peStroke.setPathEffect(EmptyPathEffect::Make(false)); peStroke.setStrokeWidth(2.f); peStroke.setStyle(SkPaint::kStroke_Style); TestCase geoPEStrokeCase(geo, peStroke, reporter); @@ -1247,6 +1263,29 @@ void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey); REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty()); REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty()); + REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedPathEffectShape().inverseFilled()); + REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().inverseFilled()); + pe.setPathEffect(EmptyPathEffect::Make(true)); + + TestCase geoPEInvertCase(geo, pe, reporter); + REPORTER_ASSERT(reporter, geoPEInvertCase.appliedFullStyleKey() == invertedEmptyKey); + REPORTER_ASSERT(reporter, geoPEInvertCase.appliedPathEffectKey() == invertedEmptyKey); + REPORTER_ASSERT(reporter, geoPEInvertCase.appliedPathEffectThenStrokeKey() == invertedEmptyKey); + REPORTER_ASSERT(reporter, geoPEInvertCase.appliedPathEffectShape().isEmpty()); + REPORTER_ASSERT(reporter, geoPEInvertCase.appliedFullStyleShape().isEmpty()); + REPORTER_ASSERT(reporter, geoPEInvertCase.appliedPathEffectShape().inverseFilled()); + REPORTER_ASSERT(reporter, geoPEInvertCase.appliedFullStyleShape().inverseFilled()); + + peStroke.setPathEffect(EmptyPathEffect::Make(true)); + TestCase geoPEInvertStrokeCase(geo, peStroke, reporter); + REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedFullStyleKey() == invertedEmptyKey); + REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedPathEffectKey() == invertedEmptyKey); + REPORTER_ASSERT(reporter, + geoPEInvertStrokeCase.appliedPathEffectThenStrokeKey() == invertedEmptyKey); + REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedPathEffectShape().isEmpty()); + REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedFullStyleShape().isEmpty()); + REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedPathEffectShape().inverseFilled()); + REPORTER_ASSERT(reporter, geoPEInvertStrokeCase.appliedFullStyleShape().inverseFilled()); } void test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) { @@ -1313,37 +1352,57 @@ void test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) { DEF_TEST(GrShape_empty_shape, reporter) { SkPath emptyPath; + SkPath invertedEmptyPath; + invertedEmptyPath.toggleInverseFillType(); SkPaint fill; TestCase fillEmptyCase(reporter, emptyPath, fill); REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty()); REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty()); REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty()); + REPORTER_ASSERT(reporter, !fillEmptyCase.baseShape().inverseFilled()); + REPORTER_ASSERT(reporter, !fillEmptyCase.appliedPathEffectShape().inverseFilled()); + REPORTER_ASSERT(reporter, !fillEmptyCase.appliedFullStyleShape().inverseFilled()); + TestCase fillInvertedEmptyCase(reporter, invertedEmptyPath, fill); + REPORTER_ASSERT(reporter, fillInvertedEmptyCase.baseShape().isEmpty()); + REPORTER_ASSERT(reporter, fillInvertedEmptyCase.appliedPathEffectShape().isEmpty()); + REPORTER_ASSERT(reporter, fillInvertedEmptyCase.appliedFullStyleShape().isEmpty()); + REPORTER_ASSERT(reporter, fillInvertedEmptyCase.baseShape().inverseFilled()); + REPORTER_ASSERT(reporter, fillInvertedEmptyCase.appliedPathEffectShape().inverseFilled()); + REPORTER_ASSERT(reporter, fillInvertedEmptyCase.appliedFullStyleShape().inverseFilled()); Key emptyKey(fillEmptyCase.baseKey()); REPORTER_ASSERT(reporter, emptyKey.count()); + Key inverseEmptyKey(fillInvertedEmptyCase.baseKey()); + REPORTER_ASSERT(reporter, inverseEmptyKey.count()); TestCase::SelfExpectations expectations; expectations.fStrokeApplies = false; expectations.fPEHasEffect = false; // This will test whether applying style preserves emptiness fillEmptyCase.testExpectations(reporter, expectations); + fillInvertedEmptyCase.testExpectations(reporter, expectations); // Stroking an empty path should have no effect - SkPath emptyPath2; SkPaint stroke; stroke.setStrokeWidth(2.f); stroke.setStyle(SkPaint::kStroke_Style); - TestCase strokeEmptyCase(reporter, emptyPath2, stroke); + TestCase strokeEmptyCase(reporter, emptyPath, stroke); strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation); + TestCase strokeInvertedEmptyCase(reporter, invertedEmptyPath, stroke); + strokeInvertedEmptyCase.compare(reporter, fillInvertedEmptyCase, + TestCase::kAllSame_ComparisonExpecation); // Dashing and stroking an empty path should have no effect - SkPath emptyPath3; SkPaint dashAndStroke; dashAndStroke.setPathEffect(make_dash()); dashAndStroke.setStrokeWidth(2.f); dashAndStroke.setStyle(SkPaint::kStroke_Style); - TestCase dashAndStrokeEmptyCase(reporter, emptyPath3, dashAndStroke); + TestCase dashAndStrokeEmptyCase(reporter, emptyPath, dashAndStroke); dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation); + TestCase dashAndStrokeInvertexEmptyCase(reporter, invertedEmptyPath, dashAndStroke); + // Dashing ignores inverseness so this is equivalent to the non-inverted empty fill. + dashAndStrokeInvertexEmptyCase.compare(reporter, fillEmptyCase, + TestCase::kAllSame_ComparisonExpecation); // A shape made from an empty rrect should behave the same as an empty path. SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty()); @@ -1351,12 +1410,24 @@ DEF_TEST(GrShape_empty_shape, reporter) { TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke); dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation); + static constexpr SkPath::Direction kDir = SkPath::kCCW_Direction; + static constexpr int kStart = 0; + TestCase dashAndStrokeEmptyInvertedRRectCase(reporter, emptyRRect, kDir, kStart, true, + GrStyle(dashAndStroke)); + // Dashing ignores inverseness so this is equivalent to the non-inverted empty fill. + dashAndStrokeEmptyInvertedRRectCase.compare(reporter, fillEmptyCase, + TestCase::kAllSame_ComparisonExpecation); // Same for a rect. SkRect emptyRect = SkRect::MakeEmpty(); TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke); dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation); + TestCase dashAndStrokeEmptyInvertedRectCase(reporter, SkRRect::MakeRect(emptyRect), kDir, + kStart, true, GrStyle(dashAndStroke)); + // Dashing ignores inverseness so this is equivalent to the non-inverted empty fill. + dashAndStrokeEmptyInvertedRectCase.compare(reporter, fillEmptyCase, + TestCase::kAllSame_ComparisonExpecation); } // rect and oval types have rrect start indices that collapse to the same point. Here we select the @@ -1651,6 +1722,13 @@ DEF_TEST(GrShape_lines, r) { fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation); REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr)); + SkPath path; + path.toggleInverseFillType(); + TestCase fillEmptyInverted(r, path, fill); + TestCase fillABInverted(r, invLineAB, fill); + fillABInverted.compare(r, fillEmptyInverted, TestCase::kAllSame_ComparisonExpecation); + REPORTER_ASSERT(r, !fillABInverted.baseShape().asLine(nullptr, nullptr)); + TestCase strokeAB(r, lineAB, stroke); TestCase strokeBA(r, lineBA, stroke); TestCase strokeAC(r, lineAC, stroke); |