diff options
-rw-r--r-- | src/gpu/GrShape.cpp | 4 | ||||
-rw-r--r-- | src/gpu/GrShape.h | 11 | ||||
-rw-r--r-- | src/gpu/GrStyle.cpp | 30 | ||||
-rw-r--r-- | src/gpu/GrStyle.h | 2 | ||||
-rw-r--r-- | src/utils/SkDashPath.cpp | 6 | ||||
-rw-r--r-- | src/utils/SkDashPathPriv.h | 18 | ||||
-rw-r--r-- | tests/GrShapeTest.cpp | 27 |
7 files changed, 79 insertions, 19 deletions
diff --git a/src/gpu/GrShape.cpp b/src/gpu/GrShape.cpp index d2bec72cbf..11b2d38971 100644 --- a/src/gpu/GrShape.cpp +++ b/src/gpu/GrShape.cpp @@ -211,8 +211,8 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) { // Should we consider bounds? Would have to include in key, but it'd be nice to know // if the bounds actually modified anything before including in key. SkStrokeRec strokeRec = parent.fStyle.strokeRec(); - strokeRec.setResScale(scale); - if (!pe->filterPath(fPath.get(), *srcForPathEffect, &strokeRec, nullptr)) { + if (!parent.fStyle.applyPathEffectToPath(fPath.get(), &strokeRec, *srcForPathEffect, + scale)) { // If the path effect fails then we continue as though there was no path effect. // If the original was a rrect that we couldn't canonicalize because of the path // effect, then do so now. diff --git a/src/gpu/GrShape.h b/src/gpu/GrShape.h index 3efb66fd6b..93896ce3f0 100644 --- a/src/gpu/GrShape.h +++ b/src/gpu/GrShape.h @@ -168,6 +168,17 @@ public: return true; } + /** + * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints. + * An inverse filled line path is still considered a line. + */ + bool asLine(SkPoint pts[2]) const { + if (fType != Type::kPath) { + return false; + } + return fPath.get()->isLine(pts); + } + /** Returns the unstyled geometry as a path. */ void asPath(SkPath* out) const { switch (fType) { diff --git a/src/gpu/GrStyle.cpp b/src/gpu/GrStyle.cpp index bb1f03dc72..10a0debe4e 100644 --- a/src/gpu/GrStyle.cpp +++ b/src/gpu/GrStyle.cpp @@ -6,6 +6,7 @@ */ #include "GrStyle.h" +#include "SkDashPathPriv.h" int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) { GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar)); @@ -120,12 +121,29 @@ void GrStyle::initPathEffect(SkPathEffect* pe) { } } -static inline bool apply_path_effect(SkPath* dst, SkStrokeRec* strokeRec, - const sk_sp<SkPathEffect>& pe, const SkPath& src) { - if (!pe) { +bool GrStyle::applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const { + if (!fPathEffect) { return false; } - if (!pe->filterPath(dst, src, strokeRec, nullptr)) { + if (SkPathEffect::kDash_DashType == fDashInfo.fType) { + // We apply the dash ourselves here rather than using the path effect. This is so that + // we can control whether the dasher applies the strokeRec for special cases. Our keying + // depends on the strokeRec being applied separately. + SkScalar phase = fDashInfo.fPhase; + const SkScalar* intervals = fDashInfo.fIntervals.get(); + int intervalCnt = fDashInfo.fIntervals.count(); + SkScalar initialLength; + int initialIndex; + SkScalar intervalLength; + SkDashPath::CalcDashParameters(phase, intervals, intervalCnt, &initialLength, + &initialIndex, &intervalLength); + if (!SkDashPath::InternalFilter(dst, src, strokeRec, + nullptr, intervals, intervalCnt, + initialLength, initialIndex, intervalLength, + SkDashPath::StrokeRecApplication::kDisallow)) { + return false; + } + } else if (!fPathEffect->filterPath(dst, src, strokeRec, nullptr)) { return false; } dst->setIsVolatile(true); @@ -137,7 +155,7 @@ bool GrStyle::applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStroke, SkASSERT(dst); SkStrokeRec strokeRec = fStrokeRec; strokeRec.setResScale(resScale); - if (!apply_path_effect(dst, &strokeRec, fPathEffect, src)) { + if (!this->applyPathEffect(dst, &strokeRec, src)) { return false; } *remainingStroke = strokeRec; @@ -151,7 +169,7 @@ bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPa SkStrokeRec strokeRec = fStrokeRec; strokeRec.setResScale(resScale); const SkPath* pathForStrokeRec = &src; - if (apply_path_effect(dst, &strokeRec, fPathEffect, src)) { + if (this->applyPathEffect(dst, &strokeRec, src)) { pathForStrokeRec = dst; } else if (fPathEffect) { return false; diff --git a/src/gpu/GrStyle.h b/src/gpu/GrStyle.h index 51908344d4..326f800442 100644 --- a/src/gpu/GrStyle.h +++ b/src/gpu/GrStyle.h @@ -201,6 +201,8 @@ private: SkAutoSTArray<4, SkScalar> fIntervals; }; + bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const; + SkStrokeRec fStrokeRec; sk_sp<SkPathEffect> fPathEffect; DashInfo fDashInfo; diff --git a/src/utils/SkDashPath.cpp b/src/utils/SkDashPath.cpp index 833c7ccce1..8317d20f37 100644 --- a/src/utils/SkDashPath.cpp +++ b/src/utils/SkDashPath.cpp @@ -216,7 +216,8 @@ private: bool SkDashPath::InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkScalar aIntervals[], int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, - SkScalar intervalLength) { + SkScalar intervalLength, + StrokeRecApplication strokeRecApplication) { // we do nothing if the src wants to be filled SkStrokeRec::Style style = rec->getStyle(); @@ -235,7 +236,8 @@ bool SkDashPath::InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec } SpecialLineRec lineRec; - bool specialLine = lineRec.init(*srcPtr, dst, rec, count >> 1, intervalLength); + bool specialLine = (StrokeRecApplication::kAllow == strokeRecApplication) && + lineRec.init(*srcPtr, dst, rec, count >> 1, intervalLength); SkPathMeasure meas(*srcPtr, false, rec->getResScale()); diff --git a/src/utils/SkDashPathPriv.h b/src/utils/SkDashPathPriv.h index 54bf9a4870..115128131f 100644 --- a/src/utils/SkDashPathPriv.h +++ b/src/utils/SkDashPathPriv.h @@ -11,7 +11,7 @@ #include "SkPathEffect.h" namespace SkDashPath { - /* + /** * Calculates the initialDashLength, initialDashIndex, and intervalLength based on the * inputed phase and intervals. If adjustedPhase is passed in, then the phase will be * adjusted to be between 0 and intervalLength. The result will be stored in adjustedPhase. @@ -26,13 +26,23 @@ namespace SkDashPath { bool FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, const SkPathEffect::DashInfo& info); - /* - * Caller should have already used ValidDashPath to exclude invalid data. + /** See comments for InternalFilter */ + enum class StrokeRecApplication { + kDisallow, + kAllow, + }; + + /** + * Caller should have already used ValidDashPath to exclude invalid data. Typically, this leaves + * the strokeRec unmodified. However, for some simple shapes (e.g. a line) it may directly + * evaluate the dash and stroke to produce a stroked output path with a fill strokeRec. Passing + * true for disallowStrokeRecApplication turns this behavior off. */ bool InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkScalar aIntervals[], int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, - SkScalar intervalLength); + SkScalar intervalLength, + StrokeRecApplication = StrokeRecApplication::kAllow); bool ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count); } diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp index 2edfd1276e..5fa1db9f7e 100644 --- a/tests/GrShapeTest.cpp +++ b/tests/GrShapeTest.cpp @@ -325,6 +325,11 @@ void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b REPORTER_ASSERT(r, a.knownToBeClosed() == b.knownToBeClosed()); 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)); + if (a.asLine(pts)) { + REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]); + } } void TestCase::compare(skiatest::Reporter* r, const TestCase& that, @@ -1269,6 +1274,8 @@ DEF_TEST(GrShape, reporter) { test_path_effect_makes_empty_shape(reporter, r); test_path_effect_fails(reporter, r); test_make_hairline_path_effect(reporter, r, true); + GrShape shape(r); + REPORTER_ASSERT(reporter, !shape.asLine(nullptr)); } for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)), @@ -1295,39 +1302,47 @@ DEF_TEST(GrShape, reporter) { test_path_effect_makes_empty_shape(reporter, rr); test_path_effect_fails(reporter, rr); test_make_hairline_path_effect(reporter, rr, true); + GrShape shape(rr); + REPORTER_ASSERT(reporter, !shape.asLine(nullptr)); } struct TestPath { - TestPath(const SkPath& path, bool isRRectFill, bool isRRectStroke, const SkRRect& rrect) + TestPath(const SkPath& path, bool isRRectFill, bool isRRectStroke, bool isLine, const SkRRect& rrect) : fPath(path) , fIsRRectForFill(isRRectFill) , fIsRRectForStroke(isRRectStroke) + , fIsLine(isLine) , fRRect(rrect) {} SkPath fPath; bool fIsRRectForFill; bool fIsRRectForStroke; + bool fIsLine; SkRRect fRRect; }; SkTArray<TestPath> paths; SkPath circlePath; circlePath.addCircle(10, 10, 10); - paths.emplace_back(circlePath, true, true, SkRRect::MakeOval(SkRect::MakeWH(20,20))); + paths.emplace_back(circlePath, true, true, false, SkRRect::MakeOval(SkRect::MakeWH(20,20))); SkPath rectPath; rectPath.addRect(SkRect::MakeWH(10, 10)); - paths.emplace_back(rectPath, true, true, SkRRect::MakeRect(SkRect::MakeWH(10, 10))); + paths.emplace_back(rectPath, true, true, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10))); SkPath openRectPath; openRectPath.moveTo(0, 0); openRectPath.lineTo(10, 0); openRectPath.lineTo(10, 10); openRectPath.lineTo(0, 10); - paths.emplace_back(openRectPath, true, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10))); + paths.emplace_back(openRectPath, true, false, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10))); SkPath quadPath; quadPath.quadTo(10, 10, 5, 8); - paths.emplace_back(quadPath, false, false, SkRRect()); + paths.emplace_back(quadPath, false, false, false, SkRRect()); + + SkPath linePath; + linePath.lineTo(10, 10); + paths.emplace_back(linePath, false, false, true, SkRRect()); for (auto testPath : paths) { for (bool inverseFill : {false, true}) { @@ -1399,6 +1414,8 @@ DEF_TEST(GrShape, reporter) { strokePathCase.compare(reporter, strokeRRectCase, TestCase::kAllSame_ComparisonExpecation); } + REPORTER_ASSERT(reporter, testPath.fIsLine == fillPathCase.baseShape().asLine(nullptr)); + REPORTER_ASSERT(reporter, testPath.fIsLine == strokePathCase.baseShape().asLine(nullptr)); } // Test a volatile empty path. |