In GrShape detect that stroked axis-aligned lines are rrects.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2151313002
Review-Url: https://codereview.chromium.org/2151313002
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 39cbb33..f25cac6 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -418,10 +418,9 @@
return r;
}
- static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
- SkRect rect;
- rect.set(l, t, r, b);
- return rect;
+ static constexpr SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r,
+ SkScalar b) {
+ return SkRect {l, t, r, b};
}
static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
diff --git a/src/gpu/GrShape.cpp b/src/gpu/GrShape.cpp
index 5ffd32d..698ff48 100644
--- a/src/gpu/GrShape.cpp
+++ b/src/gpu/GrShape.cpp
@@ -30,10 +30,12 @@
}
SkRect GrShape::bounds() const {
- static constexpr SkRect kEmpty = SkRect::MakeEmpty();
+ // Bounds where left == bottom or top == right can indicate a line or point shape. We return
+ // inverted bounds for a truly empty shape.
+ static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
switch (fType) {
case Type::kEmpty:
- return kEmpty;
+ return kInverted;
case Type::kLine: {
SkRect bounds;
if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) {
@@ -58,7 +60,7 @@
return this->path().getBounds();
}
SkFAIL("Unknown shape type");
- return kEmpty;
+ return kInverted;
}
SkRect GrShape::styledBounds() const {
@@ -158,6 +160,9 @@
if (parent.knownToBeClosed()) {
styleKeyFlags |= GrStyle::kClosed_KeyFlag;
}
+ if (parent.asLine(nullptr, nullptr)) {
+ styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
+ }
int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
if (styleCnt < 0) {
// The style doesn't allow a key, set the path gen ID to 0 so that we fail when
@@ -232,17 +237,9 @@
SkStrokeRec strokeRec = parent.fStyle.strokeRec();
if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
scale)) {
- // If the path effect fails then we continue as though there was no path effect.
- // If the original was a rrect that we couldn't canonicalize because of the path
- // effect, then do so now.
- if (parent.fType == Type::kRRect && (parent.fRRectData.fDir != kDefaultRRectDir ||
- parent.fRRectData.fStart != kDefaultRRectStart)) {
- SkASSERT(srcForPathEffect == tmpPath.get());
- tmpPath.get()->reset();
- tmpPath.get()->addRRect(parent.fRRectData.fRRect, kDefaultRRectDir,
- kDefaultRRectDir);
- }
- this->path() = *srcForPathEffect;
+ tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
+ *this = tmpParent.get()->applyStyle(apply, scale);
+ return;
}
// A path effect has access to change the res scale but we aren't expecting it to and it
// would mess up our key computation.
@@ -262,8 +259,16 @@
}
tmpParent.get()->asPath(tmpPath.get());
SkStrokeRec::InitStyle fillOrHairline;
- SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
- *tmpPath.get(), scale));
+ // The parent shape may have simplified away the strokeRec, check for that here.
+ if (tmpParent.get()->style().applies()) {
+ SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
+ *tmpPath.get(), scale));
+ } else if (tmpParent.get()->style().isSimpleFill()) {
+ fillOrHairline = SkStrokeRec::kFill_InitStyle;
+ } else {
+ SkASSERT(tmpParent.get()->style().isSimpleHairline());
+ fillOrHairline = SkStrokeRec::kHairline_InitStyle;
+ }
fStyle.resetToInitStyle(fillOrHairline);
parentForKey = tmpParent.get();
} else {
@@ -401,19 +406,76 @@
}
void GrShape::attemptToSimplifyLine() {
+ SkASSERT(Type::kLine == fType);
+ SkASSERT(!fInheritedKey.count());
+ if (fStyle.isDashed()) {
+ // Dashing ignores inverseness.
+ fLineData.fInverted = false;
+ return;
+ } else if (fStyle.hasPathEffect()) {
+ return;
+ }
+ if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
+ // Make stroke + fill be stroke since the fill is empty.
+ SkStrokeRec rec = fStyle.strokeRec();
+ rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
+ fStyle = GrStyle(rec, nullptr);
+ }
if (fStyle.isSimpleFill() && !fLineData.fInverted) {
this->changeType(Type::kEmpty);
- } else {
- // Only path effects could care about the order of the points. Otherwise canonicalize
- // the point order
- if (!fStyle.hasPathEffect()) {
- 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]);
+ 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;
}
- } else if (fStyle.isDashed()) {
- // Dashing ignores inverseness.
- fLineData.fInverted = false;
+ 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(Type::kEmpty);
+ return;
+ }
+ fStyle = GrStyle::SimpleFill();
+ return;
}
}
+ // Only path effects could care about the order of the points. Otherwise canonicalize
+ // the point order.
+ if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) {
+ SkTSwap(pts[0], pts[1]);
+ }
}
diff --git a/src/gpu/GrStyle.cpp b/src/gpu/GrStyle.cpp
index 43718db..7ea5193 100644
--- a/src/gpu/GrStyle.cpp
+++ b/src/gpu/GrStyle.cpp
@@ -75,25 +75,30 @@
GR_STATIC_ASSERT(SkPaint::kCapCount <= (1 << kCapBits));
// The cap type only matters for unclosed shapes. However, a path effect could unclose
// the shape before it is stroked.
- SkPaint::Cap cap;
- if ((flags & kClosed_KeyFlag) && !style.pathEffect()) {
- cap = SkPaint::kButt_Cap;
- } else {
+ SkPaint::Cap cap = SkPaint::kDefault_Cap;
+ if (!(flags & kClosed_KeyFlag) || style.pathEffect()) {
cap = style.strokeRec().getCap();
}
+ SkScalar miter = -1.f;
+ SkPaint::Join join = SkPaint::kDefault_Join;
+
+ // Dashing will not insert joins but other path effects may.
+ if (!(flags & kNoJoins_KeyFlag) || style.hasNonDashPathEffect()) {
+ join = style.strokeRec().getJoin();
+ // Miter limit only affects miter joins
+ if (SkPaint::kMiter_Join == join) {
+ miter = style.strokeRec().getMiter();
+ }
+ }
+
key[i++] = style.strokeRec().getStyle() |
- style.strokeRec().getJoin() << kJoinShift |
+ join << kJoinShift |
cap << kCapShift;
- SkScalar scalar;
- // Miter limit only affects miter joins
- scalar = SkPaint::kMiter_Join == style.strokeRec().getJoin()
- ? style.strokeRec().getMiter()
- : -1.f;
- memcpy(&key[i++], &scalar, sizeof(scalar));
+ memcpy(&key[i++], &miter, sizeof(miter));
- scalar = style.strokeRec().getWidth();
- memcpy(&key[i++], &scalar, sizeof(scalar));
+ SkScalar width = style.strokeRec().getWidth();
+ memcpy(&key[i++], &width, sizeof(width));
}
SkASSERT(KeySize(style, apply) == i);
}
diff --git a/src/gpu/GrStyle.h b/src/gpu/GrStyle.h
index 326f800..9091166 100644
--- a/src/gpu/GrStyle.h
+++ b/src/gpu/GrStyle.h
@@ -51,7 +51,9 @@
*/
enum KeyFlags {
// The shape being styled has no open contours.
- kClosed_KeyFlag = 0x1
+ kClosed_KeyFlag = 0x1,
+ // The shape being styled doesn't have any joins and so isn't affected by join type.
+ kNoJoins_KeyFlag = 0x2
};
/**
diff --git a/tests/DFPathRendererTest.cpp b/tests/DFPathRendererTest.cpp
index 561ec45..a616399 100644
--- a/tests/DFPathRendererTest.cpp
+++ b/tests/DFPathRendererTest.cpp
@@ -21,6 +21,9 @@
GrResourceProvider* rp) {
SkPath path;
path.lineTo(49.0255089839f, 0.473541f);
+ // This extra line wasn't in the original bug but was added to fake out GrShape's special
+ // handling of single line segments.
+ path.rLineTo(0.015f, 0.015f);
static constexpr SkScalar mvals[] = {14.0348252854f, 2.13026182736f,
13.6122547187f, 118.309922702f,
1912337682.09f, 2105391889.87f};
diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp
index ca907a1..5d1fa90 100644
--- a/tests/GrShapeTest.cpp
+++ b/tests/GrShapeTest.cpp
@@ -119,7 +119,7 @@
SkPath path;
shape.asPath(&path);
// If the bounds are empty, the path ought to be as well.
- if (bounds.isEmpty()) {
+ if (bounds.fLeft > bounds.fRight || bounds.fTop > bounds.fBottom) {
REPORTER_ASSERT(r, path.isEmpty());
return;
}
@@ -513,6 +513,13 @@
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>
static void test_scale(skiatest::Reporter* reporter, const GEO& geo) {
sk_sp<SkPathEffect> dashPE = make_dash();
@@ -520,6 +527,15 @@
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);
@@ -539,15 +555,27 @@
stroke.setStrokeWidth(2.f);
TestCase strokeCase1(geo, stroke, reporter, kS1);
TestCase strokeCase2(geo, stroke, reporter, kS2);
- // Scale affects the stroke.
- strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
+ // Scale affects the stroke
+ if (wasSimplified(strokeCase1)) {
+ REPORTER_ASSERT(reporter, wasSimplified(strokeCase2));
+ strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation);
+ } else {
+ strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
+ }
SkPaint strokeDash = stroke;
strokeDash.setPathEffect(make_dash());
TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
// Scale affects the dash and the stroke.
- strokeDashCase1.compare(reporter, strokeDashCase2, TestCase::kSameUpToPE_ComparisonExpecation);
+ if (wasSimplified(strokeDashCase1)) {
+ REPORTER_ASSERT(reporter, wasSimplified(strokeDashCase2));
+ strokeDashCase1.compare(reporter, strokeDashCase2,
+ TestCase::kAllSame_ComparisonExpecation);
+ } else {
+ strokeDashCase1.compare(reporter, strokeDashCase2,
+ TestCase::kSameUpToPE_ComparisonExpecation);
+ }
// Stroke and fill cases
SkPaint strokeAndFill = stroke;
@@ -559,22 +587,20 @@
// Dash is ignored for stroke and fill
TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
- // Scale affects the stroke. Though, this can wind up creating a rect when the input is a rect.
- // In that case we wind up with a pure geometry key and the geometries are the same.
- SkRRect rrect;
- if (strokeAndFillCase1.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr, nullptr)) {
- // We currently only expect to get here in the rect->rect case.
- REPORTER_ASSERT(reporter, rrect.isRect());
- REPORTER_ASSERT(reporter,
- strokeAndFillCase1.baseShape().asRRect(&rrect, nullptr, nullptr, nullptr) &&
- rrect.isRect());
+ // 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));
strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
TestCase::kAllSame_ComparisonExpecation);
+ strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
+ TestCase::kAllSame_ComparisonExpecation);
} else {
strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
TestCase::kSameUpToStroke_ComparisonExpecation);
- strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
- TestCase::kSameUpToStroke_ComparisonExpecation);
}
strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1,
TestCase::kAllSame_ComparisonExpecation);
@@ -601,7 +627,15 @@
TestCase strokeACase(geo, strokeA, reporter);
TestCase strokeBCase(geo, strokeB, reporter);
if (paramAffectsStroke) {
- strokeACase.compare(reporter, strokeBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
+ // If stroking is immediately incorporated into a geometric transformation then the base
+ // shapes will differ.
+ if (strokeACase.baseShape().style().applies()) {
+ strokeACase.compare(reporter, strokeBCase,
+ TestCase::kSameUpToStroke_ComparisonExpecation);
+ } else {
+ strokeACase.compare(reporter, strokeBCase,
+ TestCase::kAllDifferent_ComparisonExpecation);
+ }
} else {
strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
}
@@ -613,8 +647,15 @@
TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
if (paramAffectsStroke) {
- strokeAndFillACase.compare(reporter, strokeAndFillBCase,
- TestCase::kSameUpToStroke_ComparisonExpecation);
+ // If stroking is immediately incorporated into a geometric transformation then the base
+ // shapes will differ.
+ if (strokeAndFillACase.baseShape().style().applies()) {
+ strokeAndFillACase.compare(reporter, strokeAndFillBCase,
+ TestCase::kSameUpToStroke_ComparisonExpecation);
+ } else {
+ strokeAndFillACase.compare(reporter, strokeAndFillBCase,
+ TestCase::kAllDifferent_ComparisonExpecation);
+ }
} else {
strokeAndFillACase.compare(reporter, strokeAndFillBCase,
TestCase::kAllSame_ComparisonExpecation);
@@ -636,7 +677,13 @@
TestCase dashACase(geo, dashA, reporter);
TestCase dashBCase(geo, dashB, reporter);
if (paramAffectsDashAndStroke) {
- dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
+ // 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);
+ }
} else {
dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
}
@@ -664,6 +711,26 @@
affectsDashAndStroke);
};
+static bool shape_known_not_to_have_joins(const GrShape& shape) {
+ return shape.asLine(nullptr, nullptr) || shape.isEmpty();
+}
+
+template <typename GEO>
+static void test_stroke_join(skiatest::Reporter* reporter, const GEO& geo) {
+ GrShape shape(geo, GrStyle(SkStrokeRec::kHairline_InitStyle));
+ // GrShape recognizes certain types don't have joins and will prevent the join type from
+ // affecting the style key.
+ // Dashing doesn't add additional joins. However, GrShape currently loses track of this
+ // after applying the dash.
+ bool affectsStroke = !shape_known_not_to_have_joins(shape);
+ test_stroke_param_impl<GEO, SkPaint::Join>(
+ reporter,
+ geo,
+ [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
+ SkPaint::kRound_Join, SkPaint::kBevel_Join,
+ affectsStroke, true);
+};
+
template <typename GEO>
static void test_miter_limit(skiatest::Reporter* reporter, const GEO& geo) {
auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
@@ -676,6 +743,9 @@
p->setStrokeMiter(miter);
};
+ GrShape shape(geo, GrStyle(SkStrokeRec::kHairline_InitStyle));
+ bool mayHaveJoins = !shape_known_not_to_have_joins(shape);
+
// The miter limit should affect stroked and dashed-stroked cases when the join type is
// miter.
test_stroke_param_impl<GEO, SkScalar>(
@@ -683,7 +753,7 @@
geo,
setMiterJoinAndLimit,
0.5f, 0.75f,
- true,
+ mayHaveJoins,
true);
// The miter limit should not affect stroked and dashed-stroked cases when the join type is
@@ -991,7 +1061,7 @@
template <typename GEO>
void test_path_effect_fails(skiatest::Reporter* reporter, const GEO& geo) {
/**
- * This path effect returns an empty path.
+ * This path effect always fails to apply.
*/
class FailurePathEffect : SkPathEffect {
public:
@@ -1461,6 +1531,63 @@
}
+static void test_stroked_lines(skiatest::Reporter* 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;
+
+ TestCase(linePath, buttCap, r).compare(r, TestCase(SkRect::MakeLTRB(2, 4, 6, 5), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+
+ TestCase(linePath, squareCap, r).compare(r, TestCase(SkRect::MakeLTRB(2, 2, 6, 7), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+
+ TestCase(linePath, roundCap, r).compare(r,
+ TestCase(SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+
+ // horizontal
+ linePath.reset();
+ linePath.moveTo(4, 4);
+ linePath.lineTo(5, 4);
+
+ TestCase(linePath, buttCap, r).compare(r, TestCase(SkRect::MakeLTRB(4, 2, 5, 6), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+ TestCase(linePath, squareCap, r).compare(r, TestCase(SkRect::MakeLTRB(2, 2, 7, 6), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+ TestCase(linePath, roundCap, r).compare(r,
+ TestCase(SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+
+ // point
+ linePath.reset();
+ linePath.moveTo(4, 4);
+ linePath.lineTo(4, 4);
+
+ TestCase(linePath, buttCap, r).compare(r, TestCase(SkRect::MakeEmpty(), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+ TestCase(linePath, squareCap, r).compare(r, TestCase(SkRect::MakeLTRB(2, 2, 6, 6), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+ TestCase(linePath, roundCap, r).compare(r,
+ TestCase(SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill, r),
+ TestCase::kAllSame_ComparisonExpecation);
+}
+
DEF_TEST(GrShape, reporter) {
for (auto r : { SkRect::MakeWH(10, 20),
SkRect::MakeWH(-10, -20),
@@ -1475,10 +1602,7 @@
reporter, r,
[](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
SkIntToScalar(2), SkIntToScalar(4));
- test_stroke_param<SkRect, SkPaint::Join>(
- reporter, r,
- [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
- SkPaint::kMiter_Join, SkPaint::kRound_Join);
+ test_stroke_join(reporter, r);
test_stroke_cap(reporter, r);
test_miter_limit(reporter, r);
test_path_effect_makes_rrect(reporter, r);
@@ -1503,10 +1627,7 @@
reporter, rr,
[](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
SkIntToScalar(2), SkIntToScalar(4));
- test_stroke_param<SkRRect, SkPaint::Join>(
- reporter, rr,
- [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
- SkPaint::kMiter_Join, SkPaint::kRound_Join);
+ test_stroke_join(reporter, rr);
test_stroke_cap(reporter, rr);
test_miter_limit(reporter, rr);
test_path_effect_makes_rrect(reporter, rr);
@@ -1557,6 +1678,15 @@
linePath.lineTo(10, 10);
paths.emplace_back(linePath, false, false, true, SkRRect());
+ // Horizontal and vertical paths become rrects when stroked.
+ SkPath vLinePath;
+ vLinePath.lineTo(0, 10);
+ paths.emplace_back(vLinePath, false, false, true, SkRRect());
+
+ SkPath hLinePath;
+ hLinePath.lineTo(10, 0);
+ paths.emplace_back(hLinePath, false, false, true, SkRRect());
+
for (auto testPath : paths) {
for (bool inverseFill : {false, true}) {
if (inverseFill) {
@@ -1587,10 +1717,7 @@
reporter, path,
[](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
SkIntToScalar(2), SkIntToScalar(4));
- test_stroke_param<SkPath, SkPaint::Join>(
- reporter, path,
- [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
- SkPaint::kMiter_Join, SkPaint::kRound_Join);
+ test_stroke_join(reporter, path);
test_stroke_cap(reporter, path);
test_miter_limit(reporter, path);
test_unknown_path_effect(reporter, path);
@@ -1621,9 +1748,11 @@
strokePaint.setStrokeWidth(3.f);
strokePaint.setStyle(SkPaint::kStroke_Style);
TestCase strokePathCase(path, strokePaint, reporter);
- REPORTER_ASSERT(reporter, testPath.fIsRRectForStroke ==
- strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
- nullptr));
+ if (testPath.fIsRRectForStroke) {
+ REPORTER_ASSERT(reporter, strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
+ nullptr));
+ }
+
if (testPath.fIsRRectForStroke) {
REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
TestCase strokeRRectCase(rrect, strokePaint, reporter);
@@ -1638,6 +1767,8 @@
test_empty_shape(reporter);
test_lines(reporter);
+
+ test_stroked_lines(reporter);
}
#endif