| Index: tests/GrShapeTest.cpp
|
| diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..62440d4ba528c7458e326d4f4158fb6a80c278cd
|
| --- /dev/null
|
| +++ b/tests/GrShapeTest.cpp
|
| @@ -0,0 +1,316 @@
|
| +/*
|
| + * 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
|
|
|