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 |