Add control over whether lines are special cased in SkDashPath. Disable when called from GrShape.

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2055253002

Review-Url: https://codereview.chromium.org/2055253002
This commit is contained in:
bsalomon 2016-06-13 10:22:48 -07:00 committed by Commit bot
parent 2ec06c9b1c
commit 398e3f4b9f
7 changed files with 79 additions and 19 deletions

View File

@ -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 // 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. // if the bounds actually modified anything before including in key.
SkStrokeRec strokeRec = parent.fStyle.strokeRec(); SkStrokeRec strokeRec = parent.fStyle.strokeRec();
strokeRec.setResScale(scale); if (!parent.fStyle.applyPathEffectToPath(fPath.get(), &strokeRec, *srcForPathEffect,
if (!pe->filterPath(fPath.get(), *srcForPathEffect, &strokeRec, nullptr)) { scale)) {
// If the path effect fails then we continue as though there was no path effect. // 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 // If the original was a rrect that we couldn't canonicalize because of the path
// effect, then do so now. // effect, then do so now.

View File

@ -168,6 +168,17 @@ public:
return true; 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. */ /** Returns the unstyled geometry as a path. */
void asPath(SkPath* out) const { void asPath(SkPath* out) const {
switch (fType) { switch (fType) {

View File

@ -6,6 +6,7 @@
*/ */
#include "GrStyle.h" #include "GrStyle.h"
#include "SkDashPathPriv.h"
int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) { int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) {
GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar)); 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, bool GrStyle::applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const {
const sk_sp<SkPathEffect>& pe, const SkPath& src) { if (!fPathEffect) {
if (!pe) {
return false; 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; return false;
} }
dst->setIsVolatile(true); dst->setIsVolatile(true);
@ -137,7 +155,7 @@ bool GrStyle::applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStroke,
SkASSERT(dst); SkASSERT(dst);
SkStrokeRec strokeRec = fStrokeRec; SkStrokeRec strokeRec = fStrokeRec;
strokeRec.setResScale(resScale); strokeRec.setResScale(resScale);
if (!apply_path_effect(dst, &strokeRec, fPathEffect, src)) { if (!this->applyPathEffect(dst, &strokeRec, src)) {
return false; return false;
} }
*remainingStroke = strokeRec; *remainingStroke = strokeRec;
@ -151,7 +169,7 @@ bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPa
SkStrokeRec strokeRec = fStrokeRec; SkStrokeRec strokeRec = fStrokeRec;
strokeRec.setResScale(resScale); strokeRec.setResScale(resScale);
const SkPath* pathForStrokeRec = &src; const SkPath* pathForStrokeRec = &src;
if (apply_path_effect(dst, &strokeRec, fPathEffect, src)) { if (this->applyPathEffect(dst, &strokeRec, src)) {
pathForStrokeRec = dst; pathForStrokeRec = dst;
} else if (fPathEffect) { } else if (fPathEffect) {
return false; return false;

View File

@ -201,6 +201,8 @@ private:
SkAutoSTArray<4, SkScalar> fIntervals; SkAutoSTArray<4, SkScalar> fIntervals;
}; };
bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const;
SkStrokeRec fStrokeRec; SkStrokeRec fStrokeRec;
sk_sp<SkPathEffect> fPathEffect; sk_sp<SkPathEffect> fPathEffect;
DashInfo fDashInfo; DashInfo fDashInfo;

View File

@ -216,7 +216,8 @@ private:
bool SkDashPath::InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec, bool SkDashPath::InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
const SkRect* cullRect, const SkScalar aIntervals[], const SkRect* cullRect, const SkScalar aIntervals[],
int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, int32_t count, SkScalar initialDashLength, int32_t initialDashIndex,
SkScalar intervalLength) { SkScalar intervalLength,
StrokeRecApplication strokeRecApplication) {
// we do nothing if the src wants to be filled // we do nothing if the src wants to be filled
SkStrokeRec::Style style = rec->getStyle(); SkStrokeRec::Style style = rec->getStyle();
@ -235,7 +236,8 @@ bool SkDashPath::InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec
} }
SpecialLineRec lineRec; 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()); SkPathMeasure meas(*srcPtr, false, rec->getResScale());

View File

@ -11,7 +11,7 @@
#include "SkPathEffect.h" #include "SkPathEffect.h"
namespace SkDashPath { namespace SkDashPath {
/* /**
* Calculates the initialDashLength, initialDashIndex, and intervalLength based on the * Calculates the initialDashLength, initialDashIndex, and intervalLength based on the
* inputed phase and intervals. If adjustedPhase is passed in, then the phase will be * 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. * 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*, bool FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
const SkPathEffect::DashInfo& info); const SkPathEffect::DashInfo& info);
/* /** See comments for InternalFilter */
* Caller should have already used ValidDashPath to exclude invalid data. 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, bool InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
const SkRect* cullRect, const SkScalar aIntervals[], const SkRect* cullRect, const SkScalar aIntervals[],
int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, 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); bool ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count);
} }

View File

@ -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.knownToBeClosed() == b.knownToBeClosed());
REPORTER_ASSERT(r, a.bounds() == b.bounds()); REPORTER_ASSERT(r, a.bounds() == b.bounds());
REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask()); 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, 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_makes_empty_shape(reporter, r);
test_path_effect_fails(reporter, r); test_path_effect_fails(reporter, r);
test_make_hairline_path_effect(reporter, r, true); 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)), 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_makes_empty_shape(reporter, rr);
test_path_effect_fails(reporter, rr); test_path_effect_fails(reporter, rr);
test_make_hairline_path_effect(reporter, rr, true); test_make_hairline_path_effect(reporter, rr, true);
GrShape shape(rr);
REPORTER_ASSERT(reporter, !shape.asLine(nullptr));
} }
struct TestPath { 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) : fPath(path)
, fIsRRectForFill(isRRectFill) , fIsRRectForFill(isRRectFill)
, fIsRRectForStroke(isRRectStroke) , fIsRRectForStroke(isRRectStroke)
, fIsLine(isLine)
, fRRect(rrect) {} , fRRect(rrect) {}
SkPath fPath; SkPath fPath;
bool fIsRRectForFill; bool fIsRRectForFill;
bool fIsRRectForStroke; bool fIsRRectForStroke;
bool fIsLine;
SkRRect fRRect; SkRRect fRRect;
}; };
SkTArray<TestPath> paths; SkTArray<TestPath> paths;
SkPath circlePath; SkPath circlePath;
circlePath.addCircle(10, 10, 10); 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; SkPath rectPath;
rectPath.addRect(SkRect::MakeWH(10, 10)); 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; SkPath openRectPath;
openRectPath.moveTo(0, 0); openRectPath.moveTo(0, 0);
openRectPath.lineTo(10, 0); openRectPath.lineTo(10, 0);
openRectPath.lineTo(10, 10); openRectPath.lineTo(10, 10);
openRectPath.lineTo(0, 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; SkPath quadPath;
quadPath.quadTo(10, 10, 5, 8); 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 (auto testPath : paths) {
for (bool inverseFill : {false, true}) { for (bool inverseFill : {false, true}) {
@ -1399,6 +1414,8 @@ DEF_TEST(GrShape, reporter) {
strokePathCase.compare(reporter, strokeRRectCase, strokePathCase.compare(reporter, strokeRRectCase,
TestCase::kAllSame_ComparisonExpecation); 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. // Test a volatile empty path.