diff options
-rw-r--r-- | gm/dashing.cpp | 36 | ||||
-rw-r--r-- | src/gpu/GrShape.cpp | 103 | ||||
-rw-r--r-- | src/gpu/GrShape.h | 2 | ||||
-rw-r--r-- | tests/GrShapeTest.cpp | 134 |
4 files changed, 168 insertions, 107 deletions
diff --git a/gm/dashing.cpp b/gm/dashing.cpp index 277769198a..9a5268949f 100644 --- a/gm/dashing.cpp +++ b/gm/dashing.cpp @@ -569,6 +569,42 @@ DEF_SIMPLE_GM(dashtextcaps, canvas, 512, 512) { canvas->drawLine(8, 120, 456, 120, p); } +DEF_SIMPLE_GM(dash_line_zero_off_interval, canvas, 160, 330) { + static constexpr SkScalar kIntervals[] = {5.f, 0.f, 2.f, 0.f}; + SkPaint dashPaint; + dashPaint.setPathEffect(SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), 0.f)); + SkASSERT(dashPaint.getPathEffect()); + dashPaint.setStyle(SkPaint::kStroke_Style); + dashPaint.setStrokeWidth(20.f); + static constexpr struct { + SkPoint fA, fB; + } kLines[] = {{{0.5f, 0.5f}, {30.5f, 0.5f}}, // horizontal + {{0.5f, 0.5f}, {0.5f, 30.5f}}, // vertical + {{0.5f, 0.5f}, {0.5f, 0.5f}}, // point + {{0.5f, 0.5f}, {25.5f, 25.5f}}}; // diagonal + SkScalar pad = 5.f + dashPaint.getStrokeWidth(); + canvas->translate(pad / 2.f, pad / 2.f); + canvas->save(); + SkScalar h = 0.f; + for (const auto& line : kLines) { + h = SkTMax(h, SkScalarAbs(line.fA.fY - line.fB.fY)); + } + for (const auto& line : kLines) { + SkScalar w = SkScalarAbs(line.fA.fX - line.fB.fX); + for (auto cap : {SkPaint::kButt_Cap, SkPaint::kSquare_Cap, SkPaint::kRound_Cap}) { + dashPaint.setStrokeCap(cap); + for (auto aa : {false, true}) { + dashPaint.setAntiAlias(aa); + canvas->drawLine(line.fA, line.fB, dashPaint); + canvas->translate(0.f, pad + h); + } + } + canvas->restore(); + canvas->translate(pad + w, 0.f); + canvas->save(); + } +} + ////////////////////////////////////////////////////////////////////////////// DEF_GM(return new DashingGM;) diff --git a/src/gpu/GrShape.cpp b/src/gpu/GrShape.cpp index 4c9338409c..b9bc00d1aa 100644 --- a/src/gpu/GrShape.cpp +++ b/src/gpu/GrShape.cpp @@ -579,6 +579,13 @@ void GrShape::attemptToSimplifyLine() { SkASSERT(Type::kLine == fType); SkASSERT(!fInheritedKey.count()); if (fStyle.isDashed()) { + bool allOffsZero = true; + for (int i = 1; i < fStyle.dashIntervalCnt() && allOffsZero; i += 2) { + allOffsZero = !fStyle.dashIntervals()[i]; + } + if (allOffsZero && this->attemptToSimplifyStrokedLineToRRect()) { + return; + } // Dashing ignores inverseness. fLineData.fInverted = false; return; @@ -595,57 +602,59 @@ void GrShape::attemptToSimplifyLine() { this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty); return; } - SkPoint* pts = fLineData.fPts; - if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style) { - // If it is horizontal or vertical we will turn it into a filled rrect. - SkRect rect; - rect.fLeft = SkTMin(pts[0].fX, pts[1].fX); - rect.fRight = SkTMax(pts[0].fX, pts[1].fX); - rect.fTop = SkTMin(pts[0].fY, pts[1].fY); - rect.fBottom = SkTMax(pts[0].fY, pts[1].fY); - bool eqX = rect.fLeft == rect.fRight; - bool eqY = rect.fTop == rect.fBottom; - if (eqX || eqY) { - SkScalar r = fStyle.strokeRec().getWidth() / 2; - bool inverted = fLineData.fInverted; - this->changeType(Type::kRRect); - switch (fStyle.strokeRec().getCap()) { - case SkPaint::kButt_Cap: - if (eqX && eqY) { - this->changeType(Type::kEmpty); - return; - } - if (eqX) { - rect.outset(r, 0); - } else { - rect.outset(0, r); - } - fRRectData.fRRect = SkRRect::MakeRect(rect); - break; - case SkPaint::kSquare_Cap: - rect.outset(r, r); - fRRectData.fRRect = SkRRect::MakeRect(rect); - break; - case SkPaint::kRound_Cap: - rect.outset(r, r); - fRRectData.fRRect = SkRRect::MakeRectXY(rect, r, r); - break; - } - fRRectData.fInverted = inverted; - fRRectData.fDir = kDefaultRRectDir; - fRRectData.fStart = kDefaultRRectStart; - if (fRRectData.fRRect.isEmpty()) { - // This can happen when r is very small relative to the rect edges. - this->changeType(inverted ? Type::kInvertedEmpty : Type::kEmpty); - return; - } - fStyle = GrStyle::SimpleFill(); - return; - } + if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style && + this->attemptToSimplifyStrokedLineToRRect()) { + return; } // Only path effects could care about the order of the points. Otherwise canonicalize // the point order. + SkPoint* pts = fLineData.fPts; if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) { SkTSwap(pts[0], pts[1]); } } + +bool GrShape::attemptToSimplifyStrokedLineToRRect() { + SkASSERT(Type::kLine == fType); + SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style); + + SkRect rect; + SkVector outset; + // If we allowed a rotation angle for rrects we could capture all cases here. + if (fLineData.fPts[0].fY == fLineData.fPts[1].fY) { + rect.fLeft = SkTMin(fLineData.fPts[0].fX, fLineData.fPts[1].fX); + rect.fRight = SkTMax(fLineData.fPts[0].fX, fLineData.fPts[1].fX); + rect.fTop = rect.fBottom = fLineData.fPts[0].fY; + outset.fY = fStyle.strokeRec().getWidth() / 2.f; + outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY; + } else if (fLineData.fPts[0].fX == fLineData.fPts[1].fX) { + rect.fTop = SkTMin(fLineData.fPts[0].fY, fLineData.fPts[1].fY); + rect.fBottom = SkTMax(fLineData.fPts[0].fY, fLineData.fPts[1].fY); + rect.fLeft = rect.fRight = fLineData.fPts[0].fX; + outset.fX = fStyle.strokeRec().getWidth() / 2.f; + outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX; + } else { + return false; + } + rect.outset(outset.fX, outset.fY); + if (rect.isEmpty()) { + this->changeType(Type::kEmpty); + fStyle = GrStyle::SimpleFill(); + return true; + } + SkRRect rrect; + if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) { + SkASSERT(outset.fX == outset.fY); + rrect = SkRRect::MakeRectXY(rect, outset.fX, outset.fY); + } else { + rrect = SkRRect::MakeRect(rect); + } + bool inverted = fLineData.fInverted && !fStyle.hasPathEffect(); + this->changeType(Type::kRRect); + fRRectData.fRRect = rrect; + fRRectData.fInverted = inverted; + fRRectData.fDir = kDefaultRRectDir; + fRRectData.fStart = kDefaultRRectStart; + fStyle = GrStyle::SimpleFill(); + return true; +} diff --git a/src/gpu/GrShape.h b/src/gpu/GrShape.h index 924232e8cf..07cccb8a83 100644 --- a/src/gpu/GrShape.h +++ b/src/gpu/GrShape.h @@ -438,6 +438,8 @@ private: void attemptToSimplifyRRect(); void attemptToSimplifyLine(); + bool attemptToSimplifyStrokedLineToRRect(); + // Defaults to use when there is no distinction between even/odd and winding fills. static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType; static constexpr SkPath::FillType kDefaultPathInverseFillType = diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp index 826d0f864b..2b80a84d63 100644 --- a/tests/GrShapeTest.cpp +++ b/tests/GrShapeTest.cpp @@ -1934,66 +1934,80 @@ DEF_TEST(GrShape_lines, r) { } DEF_TEST(GrShape_stroked_lines, r) { - // Paints to try - SkPaint buttCap; - buttCap.setStyle(SkPaint::kStroke_Style); - buttCap.setStrokeWidth(4); - buttCap.setStrokeCap(SkPaint::kButt_Cap); - - SkPaint squareCap = buttCap; - squareCap.setStrokeCap(SkPaint::kSquare_Cap); - - SkPaint roundCap = buttCap; - roundCap.setStrokeCap(SkPaint::kRound_Cap); - - // vertical - SkPath linePath; - linePath.moveTo(4, 4); - linePath.lineTo(4, 5); - - SkPaint fill; - - make_TestCase(r, linePath, buttCap)->compare( - r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill), - TestCase::kAllSame_ComparisonExpecation); - - make_TestCase(r, linePath, squareCap)->compare( - r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill), - TestCase::kAllSame_ComparisonExpecation); - - make_TestCase(r, linePath, roundCap)->compare(r, - TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill), - TestCase::kAllSame_ComparisonExpecation); - - // horizontal - linePath.reset(); - linePath.moveTo(4, 4); - linePath.lineTo(5, 4); - - make_TestCase(r, linePath, buttCap)->compare( - r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill), - TestCase::kAllSame_ComparisonExpecation); - make_TestCase(r, linePath, squareCap)->compare( - r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill), - TestCase::kAllSame_ComparisonExpecation); - make_TestCase(r, linePath, roundCap)->compare( - r, TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill), - TestCase::kAllSame_ComparisonExpecation); - - // point - linePath.reset(); - linePath.moveTo(4, 4); - linePath.lineTo(4, 4); - - make_TestCase(r, linePath, buttCap)->compare( - r, TestCase(r, SkRect::MakeEmpty(), fill), - TestCase::kAllSame_ComparisonExpecation); - make_TestCase(r, linePath, squareCap)->compare( - r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill), - TestCase::kAllSame_ComparisonExpecation); - make_TestCase(r, linePath, roundCap)->compare( - r, TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill), - TestCase::kAllSame_ComparisonExpecation); + static constexpr SkScalar kIntervals1[] = {1.f, 0.f}; + auto dash1 = SkDashPathEffect::Make(kIntervals1, SK_ARRAY_COUNT(kIntervals1), 0.f); + REPORTER_ASSERT(r, dash1); + static constexpr SkScalar kIntervals2[] = {10.f, 0.f, 5.f, 0.f}; + auto dash2 = SkDashPathEffect::Make(kIntervals2, SK_ARRAY_COUNT(kIntervals2), 10.f); + REPORTER_ASSERT(r, dash2); + + sk_sp<SkPathEffect> pathEffects[] = {nullptr, std::move(dash1), std::move(dash2)}; + + for (const auto& pe : pathEffects) { + // Paints to try + SkPaint buttCap; + buttCap.setStyle(SkPaint::kStroke_Style); + buttCap.setStrokeWidth(4); + buttCap.setStrokeCap(SkPaint::kButt_Cap); + buttCap.setPathEffect(pe); + + SkPaint squareCap = buttCap; + squareCap.setStrokeCap(SkPaint::kSquare_Cap); + squareCap.setPathEffect(pe); + + SkPaint roundCap = buttCap; + roundCap.setStrokeCap(SkPaint::kRound_Cap); + roundCap.setPathEffect(pe); + + // vertical + SkPath linePath; + linePath.moveTo(4, 4); + linePath.lineTo(4, 5); + + SkPaint fill; + + make_TestCase(r, linePath, buttCap)->compare( + r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill), + TestCase::kAllSame_ComparisonExpecation); + + make_TestCase(r, linePath, squareCap)->compare( + r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill), + TestCase::kAllSame_ComparisonExpecation); + + make_TestCase(r, linePath, roundCap)->compare(r, + TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill), + TestCase::kAllSame_ComparisonExpecation); + + // horizontal + linePath.reset(); + linePath.moveTo(4, 4); + linePath.lineTo(5, 4); + + make_TestCase(r, linePath, buttCap)->compare( + r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill), + TestCase::kAllSame_ComparisonExpecation); + make_TestCase(r, linePath, squareCap)->compare( + r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill), + TestCase::kAllSame_ComparisonExpecation); + make_TestCase(r, linePath, roundCap)->compare( + r, TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill), + TestCase::kAllSame_ComparisonExpecation); + + // point + linePath.reset(); + linePath.moveTo(4, 4); + linePath.lineTo(4, 4); + + make_TestCase(r, linePath, buttCap)->compare( + r, TestCase(r, SkRect::MakeEmpty(), fill), + TestCase::kAllSame_ComparisonExpecation); + make_TestCase(r, linePath, squareCap)->compare( + r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill), + TestCase::kAllSame_ComparisonExpecation); + make_TestCase(r, linePath, roundCap)->compare( + r, TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill), + TestCase::kAllSame_ComparisonExpecation); + } } DEF_TEST(GrShape_short_path_keys, r) { |