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:
parent
2ec06c9b1c
commit
398e3f4b9f
@ -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.
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user