skia2/tests/GrShapeTest.cpp

317 lines
12 KiB
C++
Raw Normal View History

/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <initializer_list>
#include <functional>
#include "Test.h"
#if SK_SUPPORT_GPU
#include "GrShape.h"
#include "SkPath.h"
#include "SkDashPathEffect.h"
namespace {
class TestCase {
public:
TestCase(const SkRRect& rrect, const SkPaint& paint) : fBase(rrect, paint) {
this->init();
}
struct SelfExpectations {
bool fPEHasEffect;
bool fPEHasValidKey;
bool fStrokeApplies;
};
void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
enum ComparisonExpecation {
kAllDifferent_ComparisonExpecation,
kSameUpToPE_ComparisonExpecation,
kSameUpToStroke_ComparisonExpecation,
kAllSame_ComparisonExpecation,
};
void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
private:
void init() {
fAppliedPE = fBase.applyPathEffect();
fAppliedPEThenStroke = fAppliedPE.applyFullStyle();
fAppliedFull = fBase.applyFullStyle();
fBaseKeyIsValid = MakeKey(&fBaseKey, fBase);
fAppliedPEKeyIsValid = MakeKey(&fAppliedPEKey, fAppliedPE);
fAppliedPEThenStrokeKeyIsValid = MakeKey(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
fAppliedFullKeyIsValid = MakeKey(&fAppliedFullKey, fAppliedFull);
}
using Key = SkTArray<uint32_t>;
static bool MakeKey(Key* key, const GrShape& shape) {
int size = shape.unstyledKeySize();
if (size <= 0) {
return false;
}
key->reset(size);
shape.writeUnstyledKey(key->begin());
return true;
}
GrShape fBase;
GrShape fAppliedPE;
GrShape fAppliedPEThenStroke;
GrShape fAppliedFull;
Key fBaseKey;
Key fAppliedPEKey;
Key fAppliedPEThenStrokeKey;
Key fAppliedFullKey;
bool fBaseKeyIsValid;
bool fAppliedPEKeyIsValid;
bool fAppliedPEThenStrokeKeyIsValid;
bool fAppliedFullKeyIsValid;
};
void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
// Applying the path effect and then the stroke should always be the same as applying
// both in one go.
REPORTER_ASSERT(reporter, fAppliedPEThenStrokeKey == fAppliedFullKey);
// The base's key should always be valid (unless the path is volatile)
REPORTER_ASSERT(reporter, fBaseKeyIsValid);
if (expectations.fPEHasEffect) {
REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
REPORTER_ASSERT(reporter, expectations.fPEHasEffect == fAppliedPEKeyIsValid);
REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
REPORTER_ASSERT(reporter, expectations.fPEHasEffect == fAppliedFullKeyIsValid);
if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
REPORTER_ASSERT(reporter, expectations.fPEHasEffect == fAppliedFullKeyIsValid);
}
} else {
REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
if (expectations.fStrokeApplies) {
REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
} else {
REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
}
}
}
void TestCase::compare(skiatest::Reporter* reporter, const TestCase& that,
ComparisonExpecation expectation) const {
switch (expectation) {
case kAllDifferent_ComparisonExpecation:
REPORTER_ASSERT(reporter, fBaseKey != that.fBaseKey);
REPORTER_ASSERT(reporter, fAppliedPEKey != that.fAppliedPEKey);
REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
break;
case kSameUpToPE_ComparisonExpecation:
REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
REPORTER_ASSERT(reporter, fAppliedPEKey != that.fAppliedPEKey);
REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
break;
case kSameUpToStroke_ComparisonExpecation:
REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
REPORTER_ASSERT(reporter, fAppliedPEKey == that.fAppliedPEKey);
REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
break;
case kAllSame_ComparisonExpecation:
REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
REPORTER_ASSERT(reporter, fAppliedPEKey == that.fAppliedPEKey);
REPORTER_ASSERT(reporter, fAppliedFullKey == that.fAppliedFullKey);
break;
}
}
} // namespace
static sk_sp<SkPathEffect> make_dash() {
static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
static const SkScalar kPhase = 0.75;
return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
}
static sk_sp<SkPathEffect> make_null_dash() {
static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
}
static void test_basic(skiatest::Reporter* reporter, const SkRRect& rrect) {
sk_sp<SkPathEffect> dashPE = make_dash();
TestCase::SelfExpectations expectations;
SkPaint fill;
TestCase fillCase(rrect, fill);
expectations.fPEHasEffect = false;
expectations.fPEHasValidKey = false;
expectations.fStrokeApplies = false;
fillCase.testExpectations(reporter, expectations);
// Test that another GrShape instance built from the same primitive is the same.
TestCase(rrect, fill).compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
SkPaint stroke2RoundBevel;
stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
stroke2RoundBevel.setStrokeWidth(2.f);
TestCase stroke2RoundBevelCase(rrect, stroke2RoundBevel);
expectations.fPEHasValidKey = true;
expectations.fPEHasEffect = false;
expectations.fStrokeApplies = true;
stroke2RoundBevelCase.testExpectations(reporter, expectations);
TestCase(rrect, stroke2RoundBevel).compare(reporter, stroke2RoundBevelCase,
TestCase::kAllSame_ComparisonExpecation);
SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
stroke2RoundBevelDash.setPathEffect(make_dash());
TestCase stroke2RoundBevelDashCase(rrect, stroke2RoundBevelDash);
expectations.fPEHasValidKey = true;
expectations.fPEHasEffect = true;
expectations.fStrokeApplies = true;
stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
TestCase(rrect, stroke2RoundBevelDash).compare(reporter, stroke2RoundBevelDashCase,
TestCase::kAllSame_ComparisonExpecation);
fillCase.compare(reporter, stroke2RoundBevelCase,
TestCase::kSameUpToStroke_ComparisonExpecation);
fillCase.compare(reporter, stroke2RoundBevelDashCase,
TestCase::kSameUpToPE_ComparisonExpecation);
stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
TestCase::kSameUpToPE_ComparisonExpecation);
}
template <typename T>
static void test_stroke_param(skiatest::Reporter* reporter, const SkRRect& rrect,
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.
SkPaint strokeA;
strokeA.setStyle(SkPaint::kStroke_Style);
strokeA.setStrokeWidth(2.f);
setter(&strokeA, a);
SkPaint strokeB;
strokeB.setStyle(SkPaint::kStroke_Style);
strokeB.setStrokeWidth(2.f);
setter(&strokeB, b);
TestCase strokeACase(rrect, strokeA);
TestCase strokeBCase(rrect, strokeB);
strokeACase.compare(reporter, strokeBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
// Make sure stroking params don't affect fill style.
SkPaint fillA = strokeA, fillB = strokeB;
fillA.setStyle(SkPaint::kFill_Style);
fillB.setStyle(SkPaint::kFill_Style);
TestCase fillACase(rrect, fillA);
TestCase fillBCase(rrect, fillB);
fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
// Make sure just applying the dash but not stroke gives the same key for both stroking
// variations.
SkPaint dashA = strokeA, dashB = strokeB;
dashA.setPathEffect(make_dash());
dashB.setPathEffect(make_dash());
TestCase dashACase(rrect, dashA);
TestCase dashBCase(rrect, dashB);
dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
}
static void test_miter_limit(skiatest::Reporter* reporter, const SkRRect& rrect) {
// 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);
TestCase miterACase(rrect, miterA);
TestCase miterBCase(rrect, miterB);
miterACase.compare(reporter, miterBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
SkPaint noMiterA = miterA, noMiterB = miterB;
noMiterA.setStrokeJoin(SkPaint::kRound_Join);
noMiterB.setStrokeJoin(SkPaint::kRound_Join);
TestCase noMiterACase(rrect, noMiterA);
TestCase noMiterBCase(rrect, noMiterB);
noMiterACase.compare(reporter, noMiterBCase, TestCase::kAllSame_ComparisonExpecation);
SkPaint fillA = miterA, fillB = miterB;
fillA.setStyle(SkPaint::kFill_Style);
fillB.setStyle(SkPaint::kFill_Style);
TestCase fillACase(rrect, fillA);
TestCase fillBCase(rrect, fillB);
fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
}
static void test_dash_fill(skiatest::Reporter* reporter, const SkRRect& rrect) {
// A dash with no stroke should have no effect
using DashFactoryFn = sk_sp<SkPathEffect>(*)();
for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
SkPaint dashFill;
dashFill.setPathEffect((*md)());
TestCase dashFillCase(rrect, dashFill);
TestCase fillCase(rrect, SkPaint());
dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
}
}
void test_null_dash(skiatest::Reporter* reporter, const SkRRect& rrect) {
SkPaint fill;
SkPaint stroke;
stroke.setStyle(SkPaint::kStroke_Style);
stroke.setStrokeWidth(1.f);
SkPaint dash;
dash.setStyle(SkPaint::kStroke_Style);
dash.setStrokeWidth(1.f);
dash.setPathEffect(make_dash());
SkPaint nullDash;
nullDash.setStyle(SkPaint::kStroke_Style);
nullDash.setStrokeWidth(1.f);
nullDash.setPathEffect(make_null_dash());
TestCase fillCase(rrect, fill);
TestCase strokeCase(rrect, stroke);
TestCase dashCase(rrect, dash);
TestCase nullDashCase(rrect, nullDash);
nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
}
DEF_TEST(GrShape, reporter) {
sk_sp<SkPathEffect> dashPE = make_dash();
for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4)}) {
test_basic(reporter, rr);
test_dash_fill(reporter, rr);
test_null_dash(reporter, rr);
// Test modifying various stroke params.
test_stroke_param<SkScalar>(
reporter, rr,
[](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
SkIntToScalar(2), SkIntToScalar(4));
test_stroke_param<SkPaint::Cap>(
reporter, rr,
[](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
SkPaint::kButt_Cap, SkPaint::kRound_Cap);
test_stroke_param<SkPaint::Join>(
reporter, rr,
[](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
SkPaint::kMiter_Join, SkPaint::kRound_Join);
test_miter_limit(reporter, rr);
}
}
#endif