Make cap only affect the keys of GrShapes that are possibly-open

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1949193002

Review-Url: https://codereview.chromium.org/1949193002
This commit is contained in:
bsalomon 2016-05-04 13:50:29 -07:00 committed by Commit bot
parent 3452426715
commit 06077565b1
5 changed files with 116 additions and 51 deletions

View File

@ -103,7 +103,11 @@ void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply) {
return;
}
}
int styleCnt = GrStyle::KeySize(parent.fStyle, apply);
uint32_t styleKeyFlags = 0;
if (parent.knownToBeClosed()) {
styleKeyFlags |= GrStyle::kClosed_KeyFlag;
}
int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
if (styleCnt < 0) {
// The style doesn't allow a key, set the path to volatile so that we fail when
// we try to get a key for the shape.
@ -120,7 +124,7 @@ void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply) {
parentCnt * sizeof(uint32_t));
}
// Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply);
GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, styleKeyFlags);
}
}

View File

@ -120,6 +120,9 @@ public:
void asPath(SkPath* out) const {
switch (fType) {
case Type::kEmpty:
out->reset();
break;
case Type::kRRect:
out->reset();
out->addRRect(fRRect);
@ -127,12 +130,25 @@ public:
case Type::kPath:
*out = *fPath.get();
break;
case Type::kEmpty:
out->reset();
break;
}
}
/**
* Is it known that the shape has no unclosed contours. This means that it will not have
* any caps if stroked (modulo the effect of any path effect).
*/
bool knownToBeClosed() const {
switch (fType) {
case Type::kEmpty:
return true;
case Type::kRRect:
return true;
case Type::kPath:
return false;
}
return false;
}
/**
* Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
* A negative value is returned if the shape has no key (shouldn't be cached).

View File

@ -7,7 +7,7 @@
#include "GrStyle.h"
int GrStyle::KeySize(const GrStyle &style, Apply apply) {
int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) {
GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar));
int size = 0;
if (style.isDashed()) {
@ -29,7 +29,7 @@ int GrStyle::KeySize(const GrStyle &style, Apply apply) {
return size;
}
void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply) {
void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply, uint32_t flags) {
SkASSERT(key);
SkASSERT(KeySize(style, apply) >= 0);
GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar));
@ -63,9 +63,17 @@ void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply) {
GR_STATIC_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits));
GR_STATIC_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits));
GR_STATIC_ASSERT(SkPaint::kCapCount <= (1 << kCapBits));
// The cap type only matters for unclosed shapes. However, a path effect could unclose
// the shape before it is stroked.
SkPaint::Cap cap;
if ((flags & kClosed_KeyFlag) && !style.pathEffect()) {
cap = SkPaint::kButt_Cap;
} else {
cap = style.strokeRec().getCap();
}
key[i++] = style.strokeRec().getStyle() |
style.strokeRec().getJoin() << kJoinShift |
style.strokeRec().getCap() << kCapShift;
cap << kCapShift;
SkScalar scalar;
// Miter limit only affects miter joins

View File

@ -48,22 +48,31 @@ public:
kPathEffectAndStrokeRec
};
/**
* Optional flags for computing keys that may remove unnecessary variation in the key due to
* style settings that don't affect particular classes of geometry.
*/
enum KeyFlags {
// The shape being styled has no open contours.
kClosed_KeyFlag = 0x1
};
/**
* Computes the key length for a GrStyle. The return will be negative if it cannot be turned
* into a key. This occurs when there is a path effect that is not a dash. The key can
* either reflect just the path effect (if one) or the path effect and the strokerec. Note
* that a simple fill has a zero sized key.
*/
static int KeySize(const GrStyle& , Apply);
static int KeySize(const GrStyle& , Apply, uint32_t flags = 0);
/**
* Writes a unique key for the style into the provided buffer. This function assumes the buffer
* has room for at least KeySize() values. It assumes that KeySize() returns a non-negative
* value for the style and Apply param. This is written so that the key for just dash
* application followed by the key for the remaining SkStrokeRec is the same as the key for
* applying dashing and SkStrokeRec all at once.
* value for the combination of GrStyle, Apply and flags params. This is written so that the key
* for just dash application followed by the key for the remaining SkStrokeRec is the same as
* the key for applying dashing and SkStrokeRec all at once.
*/
static void WriteKey(uint32_t*, const GrStyle&, Apply);
static void WriteKey(uint32_t*, const GrStyle&, Apply, uint32_t flags = 0);
GrStyle() : GrStyle(SkStrokeRec::kFill_InitStyle) {}

View File

@ -263,10 +263,12 @@ static void test_basic(skiatest::Reporter* reporter, const GEO& geo) {
}
template <typename GEO, typename T>
static void test_stroke_param(skiatest::Reporter* reporter, const GEO& geo,
std::function<void(SkPaint*, T)> setter, T a, T b) {
// Set the stroke width so that we don't get hairline. However, call the function second so that
// it can override.
static void test_stroke_param_impl(skiatest::Reporter* reporter, const GEO& geo,
std::function<void(SkPaint*, T)> setter, T a, T b,
bool paramAffectsStroke,
bool paramAffectsDashAndStroke) {
// Set the stroke width so that we don't get hairline. However, call the setter afterward so
// that it can override the stroke width.
SkPaint strokeA;
strokeA.setStyle(SkPaint::kStroke_Style);
strokeA.setStrokeWidth(2.f);
@ -278,7 +280,11 @@ static void test_stroke_param(skiatest::Reporter* reporter, const GEO& geo,
TestCase strokeACase(geo, strokeA, reporter);
TestCase strokeBCase(geo, strokeB, reporter);
if (paramAffectsStroke) {
strokeACase.compare(reporter, strokeBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
} else {
strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
}
// Make sure stroking params don't affect fill style.
SkPaint fillA = strokeA, fillB = strokeB;
@ -295,38 +301,66 @@ static void test_stroke_param(skiatest::Reporter* reporter, const GEO& geo,
dashB.setPathEffect(make_dash());
TestCase dashACase(geo, dashA, reporter);
TestCase dashBCase(geo, dashB, reporter);
if (paramAffectsDashAndStroke) {
dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
} else {
dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
}
}
template <typename GEO, typename T>
static void test_stroke_param(skiatest::Reporter* reporter, const GEO& geo,
std::function<void(SkPaint*, T)> setter, T a, T b) {
test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
};
template <typename GEO>
static void test_stroke_cap(skiatest::Reporter* reporter, const GEO& geo) {
GrShape shape(geo, GrStyle(SkStrokeRec::kHairline_InitStyle));
// The cap should only affect shapes that may be open.
bool affectsStroke = !shape.knownToBeClosed();
// Dashing adds ends that need caps.
bool affectsDashAndStroke = true;
test_stroke_param_impl<GEO, SkPaint::Cap>(
reporter,
geo,
[](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
SkPaint::kButt_Cap, SkPaint::kRound_Cap,
affectsStroke,
affectsDashAndStroke);
};
template <typename GEO>
static void test_miter_limit(skiatest::Reporter* reporter, const GEO& geo) {
// Miter limit should only matter when stroking with miter joins. It shouldn't affect other
// joins or fills.
SkPaint miterA;
miterA.setStyle(SkPaint::kStroke_Style);
miterA.setStrokeWidth(2.f);
miterA.setStrokeJoin(SkPaint::kMiter_Join);
miterA.setStrokeMiter(0.5f);
SkPaint miterB = miterA;
miterA.setStrokeMiter(0.6f);
auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
p->setStrokeJoin(SkPaint::kMiter_Join);
p->setStrokeMiter(miter);
};
TestCase miterACase(geo, miterA, reporter);
TestCase miterBCase(geo, miterB, reporter);
miterACase.compare(reporter, miterBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
p->setStrokeJoin(SkPaint::kRound_Join);
p->setStrokeMiter(miter);
};
SkPaint noMiterA = miterA, noMiterB = miterB;
noMiterA.setStrokeJoin(SkPaint::kRound_Join);
noMiterB.setStrokeJoin(SkPaint::kRound_Join);
TestCase noMiterACase(geo, noMiterA, reporter);
TestCase noMiterBCase(geo, noMiterB, reporter);
noMiterACase.compare(reporter, noMiterBCase, TestCase::kAllSame_ComparisonExpecation);
// The miter limit should affect stroked and dashed-stroked cases when the join type is
// miter.
test_stroke_param_impl<GEO, SkScalar>(
reporter,
geo,
setMiterJoinAndLimit,
0.5f, 0.75f,
true,
true);
SkPaint fillA = miterA, fillB = miterB;
fillA.setStyle(SkPaint::kFill_Style);
fillB.setStyle(SkPaint::kFill_Style);
TestCase fillACase(geo, fillA, reporter);
TestCase fillBCase(geo, fillB, reporter);
fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
// The miter limit should not affect stroked and dashed-stroked cases when the join type is
// not miter.
test_stroke_param_impl<GEO, SkScalar>(
reporter,
geo,
setOtherJoinAndLimit,
0.5f, 0.75f,
false,
false);
}
template<typename GEO>
@ -660,14 +694,11 @@ DEF_TEST(GrShape, reporter) {
reporter, rr,
[](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
SkIntToScalar(2), SkIntToScalar(4));
test_stroke_param<SkRRect, SkPaint::Cap>(
reporter, rr,
[](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
SkPaint::kButt_Cap, SkPaint::kRound_Cap);
test_stroke_param<SkRRect, SkPaint::Join>(
reporter, rr,
[](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
SkPaint::kMiter_Join, SkPaint::kRound_Join);
test_stroke_cap(reporter, rr);
test_miter_limit(reporter, rr);
test_path_effect_makes_rrect(reporter, rr);
test_unknown_path_effect(reporter, rr);
@ -725,14 +756,11 @@ DEF_TEST(GrShape, reporter) {
reporter, path,
[](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
SkIntToScalar(2), SkIntToScalar(4));
test_stroke_param<SkPath, SkPaint::Cap>(
reporter, path,
[](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
SkPaint::kButt_Cap, SkPaint::kRound_Cap);
test_stroke_param<SkPath, SkPaint::Join>(
reporter, path,
[](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
SkPaint::kMiter_Join, SkPaint::kRound_Join);
test_stroke_cap(reporter, path);
test_miter_limit(reporter, path);
test_unknown_path_effect(reporter, path);
test_path_effect_makes_empty_shape(reporter, path);