aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/GrShapeTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/GrShapeTest.cpp')
-rw-r--r--tests/GrShapeTest.cpp315
1 files changed, 315 insertions, 0 deletions
diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp
new file mode 100644
index 0000000000..8c81c51ed3
--- /dev/null
+++ b/tests/GrShapeTest.cpp
@@ -0,0 +1,315 @@
+/*
+ * 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
+ 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);
+ 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