Chromium Code Reviews| Index: tests/GrShapeTest.cpp |
| diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp |
| index 5d1fa908be434ac34f2251fdd9c856fa218762b8..48a864599635ce86d0709a24be4a136d2b3ad886 100644 |
| --- a/tests/GrShapeTest.cpp |
| +++ b/tests/GrShapeTest.cpp |
| @@ -433,6 +433,74 @@ static sk_sp<SkPathEffect> make_null_dash() { |
| return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f); |
| } |
| +////////////////////////////////////////////////////////////////////////////// |
| +// These functions allow tests to check for special cases where style gets |
| +// applied by GrShape in its constructor (without calling GrShape::applyStyle). |
| +// These unfortunately rely on knowing details of GrShape's implementation. |
| +// These predicates are factored out here to avoid littering the rest of the |
|
robertphillips
2016/07/19 17:36:05
implemantation ?
bsalomon
2016/07/20 14:00:20
It's like mansplaining.
|
| +// test code with GrShape implemantation details. |
| + |
| +static bool path_is_axis_aligned_line(const SkPath& path) { |
| + SkPoint pts[2]; |
| + if (!path.isLine(pts)) { |
| + return false; |
| + } |
| + return pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY; |
| +} |
| + |
| +static bool path_is_unclosed_rect(const SkPath& path) { |
| + bool closed; |
| + return path.isRect(nullptr, &closed, nullptr) && !closed; |
| +} |
| + |
| +// Will a GrShape constructed from a geometry perform a geometric transformation if the style is |
| +// simple fill that would not otherwise be applied. |
| +template <typename GEO> static bool fill_changes_geom(const GEO& geo) { return false; } |
| +template <> bool fill_changes_geom<SkPath>(const SkPath& path) { |
| + // unclosed rects get closed. Lines get turned into empty geometry |
| + return path_is_unclosed_rect(path) || (path.isLine(nullptr) && !path.isInverseFillType()); |
| +} |
| + |
| +// Will a GrShape constructed from the geometry with a stroke style (without path effect) perform a |
| +// geometric transformation that applies the the stroke immediately without storing a stroke style. |
| +template <typename GEO> static bool stroke_is_converted_to_fill(const GEO& geo) { return false; } |
| +template <> bool stroke_is_converted_to_fill(const SkPath& path) { |
|
robertphillips
2016/07/19 17:36:05
// converted to a rect ?
bsalomon
2016/07/20 14:00:20
Done.
|
| + return path_is_axis_aligned_line(path); |
| +} |
| + |
| +// Will a GrShape constructed from the geometry with a stroke-and-fill style (without path effect) |
| +// perform a geometric transformation that applies the the stroke immediately without storing a |
| +// stroke-and-fill style. |
| +template <typename GEO> static bool stroke_and_fill_is_converted_to_fill(const GEO& geo, |
| + const SkPaint& paint); |
| +template <> bool stroke_and_fill_is_converted_to_fill(const SkRect& rect, const SkPaint& paint) { |
| + SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); |
|
robertphillips
2016/07/19 17:36:05
// converted to a bigger rect ?
bsalomon
2016/07/20 14:00:20
Done.
|
| + return paint.getStrokeJoin() == SkPaint::kMiter_Join && |
| + paint.getStrokeMiter() >= SK_ScalarSqrt2; |
| +} |
| +template <> bool stroke_and_fill_is_converted_to_fill(const SkPath& path, const SkPaint& paint) { |
| + SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); |
| + if (path_is_axis_aligned_line(path)) { |
|
robertphillips
2016/07/19 17:36:05
// converted to a rect ?
bsalomon
2016/07/20 14:00:20
Done.
|
| + return true; |
| + } |
| + SkRect rect; |
| + unsigned start; |
| + SkPath::Direction dir; |
| + if (SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start)) { |
| + return stroke_and_fill_is_converted_to_fill<SkRect>(rect, paint); |
| + } |
| + return false; |
| +} |
| +template <> bool stroke_and_fill_is_converted_to_fill(const SkRRect& rr, const SkPaint& paint) { |
| + SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); |
| + if (rr.isRect()) { |
| + return stroke_and_fill_is_converted_to_fill<SkRect>(rr.rect(), paint); |
| + } |
| + return false; |
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| template<typename GEO> |
| static void test_basic(skiatest::Reporter* reporter, const GEO& geo) { |
| sk_sp<SkPathEffect> dashPE = make_dash(); |
| @@ -457,7 +525,7 @@ static void test_basic(skiatest::Reporter* reporter, const GEO& geo) { |
| TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter); |
| expectations.fPEHasValidKey = true; |
| expectations.fPEHasEffect = false; |
| - expectations.fStrokeApplies = true; |
| + expectations.fStrokeApplies = !stroke_is_converted_to_fill(geo); |
| stroke2RoundBevelCase.testExpectations(reporter, expectations); |
| TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase, |
| TestCase::kAllSame_ComparisonExpecation); |
| @@ -472,12 +540,24 @@ static void test_basic(skiatest::Reporter* reporter, const GEO& geo) { |
| TestCase(geo, stroke2RoundBevelDash, reporter).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); |
| + if (fill_changes_geom(geo) || stroke_is_converted_to_fill(geo)) { |
| + fillCase.compare(reporter, stroke2RoundBevelCase, |
| + TestCase::kAllDifferent_ComparisonExpecation); |
| + fillCase.compare(reporter, stroke2RoundBevelDashCase, |
| + TestCase::kAllDifferent_ComparisonExpecation); |
| + } else { |
| + fillCase.compare(reporter, stroke2RoundBevelCase, |
| + TestCase::kSameUpToStroke_ComparisonExpecation); |
| + fillCase.compare(reporter, stroke2RoundBevelDashCase, |
| + TestCase::kSameUpToPE_ComparisonExpecation); |
| + } |
| + if (stroke_is_converted_to_fill(geo)) { |
| + stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase, |
| + TestCase::kAllDifferent_ComparisonExpecation); |
| + } else { |
| + stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase, |
| + TestCase::kSameUpToPE_ComparisonExpecation); |
| + } |
| // Stroke and fill cases |
| SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel; |
| @@ -485,7 +565,7 @@ static void test_basic(skiatest::Reporter* reporter, const GEO& geo) { |
| TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter); |
| expectations.fPEHasValidKey = true; |
| expectations.fPEHasEffect = false; |
| - expectations.fStrokeApplies = true; |
| + expectations.fStrokeApplies = !stroke_is_converted_to_fill(geo); |
| stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations); |
| TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter, |
| stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation); |
| @@ -495,7 +575,7 @@ static void test_basic(skiatest::Reporter* reporter, const GEO& geo) { |
| TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter); |
| expectations.fPEHasValidKey = true; |
| expectations.fPEHasEffect = false; |
| - expectations.fStrokeApplies = true; |
| + expectations.fStrokeApplies = !stroke_is_converted_to_fill(geo); |
| stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations); |
| TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare( |
| reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation); |
| @@ -506,18 +586,17 @@ static void test_basic(skiatest::Reporter* reporter, const GEO& geo) { |
| hairline.setStyle(SkPaint::kStroke_Style); |
| hairline.setStrokeWidth(0.f); |
| TestCase hairlineCase(geo, hairline, reporter); |
| - // Since hairline style doesn't change the SkPath data, it is keyed identically to fill. |
| - hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation); |
| + // Since hairline style doesn't change the SkPath data, it is keyed identically to fill (except |
| + // in the line and unclosed rect cases). |
| + if (fill_changes_geom(geo)) { |
| + hairlineCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation); |
| + } else { |
| + hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation); |
| + } |
| REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline()); |
| REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline()); |
| REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline()); |
| -} |
| -// Was the shape pre-style geometry stored as something other than a general path. This somewhat |
| -// relies on knowing the internals of GrShape to know that this combination of tests is sufficient. |
| -static bool is_non_path(const GrShape& shape) { |
| - return shape.asRRect(nullptr, nullptr, nullptr, nullptr) || shape.asLine(nullptr, nullptr) || |
| - shape.isEmpty(); |
| } |
| template<typename GEO> |
| @@ -527,15 +606,6 @@ static void test_scale(skiatest::Reporter* reporter, const GEO& geo) { |
| static const SkScalar kS1 = 1.f; |
| static const SkScalar kS2 = 2.f; |
| - // Scale may affect the key for stroked results. However, there are two ways in which that may |
| - // not occur. The base shape may instantly recognized that the geo + stroke is equivalent to |
| - // a simple filled geometry. An example is a stroked line may become a filled rrect. |
| - // Alternatively, after applying the style the output path may be recognized as a simpler shape |
| - // causing the shape with style applied to have a purely geometric key rather than a key derived |
| - // from the base geometry and the style params (and scale factor). |
| - auto wasSimplified = [](const TestCase& c) { |
| - return !c.baseShape().style().applies() || is_non_path(c.appliedFullStyleShape()); |
| - }; |
| SkPaint fill; |
| TestCase fillCase1(geo, fill, reporter, kS1); |
| TestCase fillCase2(geo, fill, reporter, kS2); |
| @@ -556,8 +626,8 @@ static void test_scale(skiatest::Reporter* reporter, const GEO& geo) { |
| TestCase strokeCase1(geo, stroke, reporter, kS1); |
| TestCase strokeCase2(geo, stroke, reporter, kS2); |
| // Scale affects the stroke |
| - if (wasSimplified(strokeCase1)) { |
| - REPORTER_ASSERT(reporter, wasSimplified(strokeCase2)); |
| + if (stroke_is_converted_to_fill(geo)) { |
| + REPORTER_ASSERT(reporter, !strokeCase1.baseShape().style().applies()); |
| strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation); |
| } else { |
| strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation); |
| @@ -568,14 +638,8 @@ static void test_scale(skiatest::Reporter* reporter, const GEO& geo) { |
| TestCase strokeDashCase1(geo, strokeDash, reporter, kS1); |
| TestCase strokeDashCase2(geo, strokeDash, reporter, kS2); |
| // Scale affects the dash and the stroke. |
| - if (wasSimplified(strokeDashCase1)) { |
| - REPORTER_ASSERT(reporter, wasSimplified(strokeDashCase2)); |
| - strokeDashCase1.compare(reporter, strokeDashCase2, |
| - TestCase::kAllSame_ComparisonExpecation); |
| - } else { |
| - strokeDashCase1.compare(reporter, strokeDashCase2, |
| - TestCase::kSameUpToPE_ComparisonExpecation); |
| - } |
| + strokeDashCase1.compare(reporter, strokeDashCase2, |
| + TestCase::kSameUpToPE_ComparisonExpecation); |
| // Stroke and fill cases |
| SkPaint strokeAndFill = stroke; |
| @@ -587,13 +651,11 @@ static void test_scale(skiatest::Reporter* reporter, const GEO& geo) { |
| // Dash is ignored for stroke and fill |
| TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1); |
| TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2); |
| - // Scale affects the stroke. Scale affects the stroke, but check to make sure this didn't |
| - // become a simpler shape (e.g. stroke-and-filled rect can become a rect), in which case the |
| - // scale shouldn't matter and the geometries should agree. |
| - if (wasSimplified(strokeAndFillCase1)) { |
| - REPORTER_ASSERT(reporter, wasSimplified(strokeAndFillCase1)); |
| - REPORTER_ASSERT(reporter, wasSimplified(strokeAndFillDashCase1)); |
| - REPORTER_ASSERT(reporter, wasSimplified(strokeAndFillDashCase2)); |
| + // Scale affects the stroke, but check to make sure this didn't become a simpler shape (e.g. |
| + // stroke-and-filled rect can become a rect), in which case the scale shouldn't matter and the |
| + // geometries should agree. |
| + if (stroke_and_fill_is_converted_to_fill(geo, strokeAndFillDash)) { |
| + REPORTER_ASSERT(reporter, !strokeAndFillCase1.baseShape().style().applies()); |
| strokeAndFillCase1.compare(reporter, strokeAndFillCase2, |
| TestCase::kAllSame_ComparisonExpecation); |
| strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2, |
| @@ -629,12 +691,12 @@ static void test_stroke_param_impl(skiatest::Reporter* reporter, const GEO& geo, |
| if (paramAffectsStroke) { |
| // If stroking is immediately incorporated into a geometric transformation then the base |
| // shapes will differ. |
| - if (strokeACase.baseShape().style().applies()) { |
| + if (stroke_is_converted_to_fill(geo)) { |
| strokeACase.compare(reporter, strokeBCase, |
| - TestCase::kSameUpToStroke_ComparisonExpecation); |
| + TestCase::kAllDifferent_ComparisonExpecation); |
| } else { |
| strokeACase.compare(reporter, strokeBCase, |
| - TestCase::kAllDifferent_ComparisonExpecation); |
| + TestCase::kSameUpToStroke_ComparisonExpecation); |
| } |
| } else { |
| strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation); |
| @@ -649,12 +711,13 @@ static void test_stroke_param_impl(skiatest::Reporter* reporter, const GEO& geo, |
| if (paramAffectsStroke) { |
| // If stroking is immediately incorporated into a geometric transformation then the base |
| // shapes will differ. |
| - if (strokeAndFillACase.baseShape().style().applies()) { |
| + if (stroke_and_fill_is_converted_to_fill(geo, strokeAndFillA) || |
| + stroke_and_fill_is_converted_to_fill(geo, strokeAndFillB)) { |
| strokeAndFillACase.compare(reporter, strokeAndFillBCase, |
| - TestCase::kSameUpToStroke_ComparisonExpecation); |
| + TestCase::kAllDifferent_ComparisonExpecation); |
| } else { |
| strokeAndFillACase.compare(reporter, strokeAndFillBCase, |
| - TestCase::kAllDifferent_ComparisonExpecation); |
| + TestCase::kSameUpToStroke_ComparisonExpecation); |
| } |
| } else { |
| strokeAndFillACase.compare(reporter, strokeAndFillBCase, |
| @@ -677,13 +740,7 @@ static void test_stroke_param_impl(skiatest::Reporter* reporter, const GEO& geo, |
| TestCase dashACase(geo, dashA, reporter); |
| TestCase dashBCase(geo, dashB, reporter); |
| if (paramAffectsDashAndStroke) { |
| - // If stroking is immediately incorporated into a geometric transformation then the base |
| - // shapes will differ. |
| - if (dashACase.baseShape().style().applies()) { |
| - dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation); |
| - } else { |
| - dashACase.compare(reporter, dashBCase, TestCase::kAllDifferent_ComparisonExpecation); |
| - } |
| + dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation); |
| } else { |
| dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation); |
| } |
| @@ -801,9 +858,21 @@ void test_null_dash(skiatest::Reporter* reporter, const GEO& geo) { |
| TestCase dashCase(geo, dash, reporter); |
| TestCase nullDashCase(geo, nullDash, reporter); |
| - nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation); |
| + // We expect the null dash to be ignored so nullDashCase should match strokeCase, always. |
| nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation); |
| - nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation); |
| + // Check whether the fillCase or strokeCase/nullDashCase would undergo a geometric tranformation |
| + // on construction in order to determine how to compare the fill and stroke. |
| + if (fill_changes_geom(geo) || stroke_is_converted_to_fill(geo)) { |
| + nullDashCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation); |
| + } else { |
| + nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation); |
| + } |
| + // In the null dash case we may immediately convert to a fill, but not for the normal dash case. |
| + if (stroke_is_converted_to_fill(geo)) { |
| + nullDashCase.compare(reporter, dashCase, TestCase::kAllDifferent_ComparisonExpecation); |
| + } else { |
| + nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation); |
| + } |
| } |
| template <typename GEO> |
| @@ -848,8 +917,16 @@ void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const GEO& geo) |
| peStroke.setStyle(SkPaint::kStroke_Style); |
| TestCase geoPEStrokeCase(geo, peStroke, reporter); |
| - fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation); |
| - fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation); |
| + // Check whether constructing the filled case would cause the base shape to have a different |
| + // geometry (because of a geometric transformation upon initial GrShape construction). |
| + if (fill_changes_geom(geo)) { |
| + fillGeoCase.compare(reporter, geoPECase, TestCase::kAllDifferent_ComparisonExpecation); |
| + fillGeoCase.compare(reporter, geoPEStrokeCase, |
| + TestCase::kAllDifferent_ComparisonExpecation); |
| + } else { |
| + fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation); |
| + fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation); |
| + } |
| geoPECase.compare(reporter, geoPEStrokeCase, |
| TestCase::kSameUpToStroke_ComparisonExpecation); |
| @@ -1197,6 +1274,9 @@ void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) { |
| strokeRecs[kStroke].setStrokeStyle(2.f); |
| strokeRecs[kHairline].setHairlineStyle(); |
| strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true); |
| + // Use a bevel join to avoid complications of stroke+filled rects becoming filled rects before |
| + // applyStyle() is called. |
| + strokeRecs[kStrokeAndFill].setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 1.f); |
| sk_sp<SkPathEffect> dashEffect = make_dash(); |
| static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs)); |
| @@ -1698,16 +1778,9 @@ DEF_TEST(GrShape, reporter) { |
| } |
| } |
| const SkPath& path = testPath.fPath; |
| - // These tests all assume that the original GrShape for fill and stroke will be the |
| - // same. |
| - // However, that is not the case in special cases (e.g. an unclosed rect becomes a RRect |
| - // GrShape with a fill style but becomes a Path GrShape when stroked). Similarly, a path |
| - // that is a line becomes empty when filled but is special-cased as a line when stroked. |
| - if (testPath.fIsRRectForFill == testPath.fIsRRectForStroke && !testPath.fIsLine) { |
| - test_basic(reporter, path); |
| - test_null_dash(reporter, path); |
| - test_path_effect_makes_rrect(reporter, path); |
| - } |
| + test_basic(reporter, path); |
| + test_null_dash(reporter, path); |
| + test_path_effect_makes_rrect(reporter, path); |
| test_scale(reporter, path); |
| // This test uses a stroking paint, hence use of fIsRRectForStroke |
| test_volatile_path(reporter, path, testPath.fIsRRectForStroke || testPath.fIsLine); |