Chromium Code Reviews| Index: tests/GrShapeTest.cpp |
| diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8a616ad92c1af78b97949d470710fd2cbb3e526c |
| --- /dev/null |
| +++ b/tests/GrShapeTest.cpp |
| @@ -0,0 +1,319 @@ |
| +/* |
| + * 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 "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 == |
|
robertphillips
2016/04/25 12:07:06
one line ?
bsalomon
2016/04/25 13:32:27
Done.
|
| + 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); |
|
robertphillips
2016/04/25 12:07:06
1 line ? + the 3 clones below ?
bsalomon
2016/04/25 13:32:27
Done.
|
| + 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 |
| + |
|
robertphillips
2016/04/25 12:07:06
static ?
bsalomon
2016/04/25 13:32:27
Done.
|
| +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); |
| +} |
| + |
|
robertphillips
2016/04/25 12:07:06
static ?
bsalomon
2016/04/25 13:32:27
Done.
|
| +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.5); |
| + SkPaint miterB = miterA; |
| + miterA.setStrokeMiter(0.6); |
| + |
| + 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 |
| + for (auto 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); |
|
robertphillips
2016/04/25 12:07:06
why two test_basic(reporter, rr) calls ?
bsalomon
2016/04/25 13:32:27
Done.
|
| + test_basic(reporter, rr); |
| + test_dash_fill(reporter, rr); |
| + test_null_dash(reporter, rr); |
|
robertphillips
2016/04/25 12:07:06
setting modifying -> modifying ?
bsalomon
2016/04/25 13:32:27
Done.
|
| + // Test setting 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 |