aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brian Salomon <bsalomon@google.com>2018-04-26 15:22:04 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-04-26 19:50:23 +0000
commite4949406ebcc5d5d8f83c1cd96209763d278b1e2 (patch)
tree758eb7f1bc477b258435ac248d0392c41aa18506
parent8f379d4d642d83fcf712d819ced716a9df01e234 (diff)
Revert "Reland "Revert "Add arcs as a specialized geometry to GrShape."""
This reverts commit 580aee2fa4a57bf8208498fbc23acea04e16e092. Bug: skia:7794 Change-Id: I9c2b923859c826dff58c22c529dc4e2ab4d0f186 Reviewed-on: https://skia-review.googlesource.com/124042 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
-rw-r--r--src/core/SkPath.cpp45
-rw-r--r--src/core/SkPathPriv.h6
-rw-r--r--src/gpu/GrRenderTargetContext.cpp7
-rw-r--r--src/gpu/GrShape.cpp91
-rw-r--r--src/gpu/GrShape.h58
-rw-r--r--tests/GrShapeTest.cpp110
6 files changed, 297 insertions, 20 deletions
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 3e4f990930..497037a09d 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1304,6 +1304,25 @@ void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
SkPoint singlePt;
+ // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
+ // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
+ // arcs from the same oval.
+ auto addPt = [&forceMoveTo, this](const SkPoint& pt) {
+ SkPoint lastPt;
+#ifdef SK_DISABLE_ARC_TO_LINE_TO_CHECK
+ static constexpr bool kSkipLineToCheck = true;
+#else
+ static constexpr bool kSkipLineToCheck = false;
+#endif
+ if (forceMoveTo) {
+ this->moveTo(pt);
+ } else if (kSkipLineToCheck || !this->getLastPt(&lastPt) ||
+ !SkScalarNearlyEqual(lastPt.fX, pt.fX) ||
+ !SkScalarNearlyEqual(lastPt.fY, pt.fY)) {
+ this->lineTo(pt);
+ }
+ };
+
// At this point, we know that the arc is not a lone point, but startV == stopV
// indicates that the sweepAngle is too small such that angles_to_unit_vectors
// cannot handle it.
@@ -1318,7 +1337,7 @@ void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
// make sin(endAngle) to be 0 which will then draw a dot.
singlePt.set(oval.centerX() + radiusX * sk_float_cos(endAngle),
oval.centerY() + radiusY * sk_float_sin(endAngle));
- forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
+ addPt(singlePt);
return;
}
@@ -1327,12 +1346,12 @@ void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
if (count) {
this->incReserve(count * 2 + 1);
const SkPoint& pt = conics[0].fPts[0];
- forceMoveTo ? this->moveTo(pt) : this->lineTo(pt);
+ addPt(pt);
for (int i = 0; i < count; ++i) {
this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
}
} else {
- forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
+ addPt(singlePt);
}
}
@@ -3343,6 +3362,20 @@ bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Di
return true;
}
+bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
+ if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
+ // This gets converted to an oval.
+ return true;
+ }
+ if (useCenter) {
+ // This is a pie wedge. It's convex if the angle is <= 180.
+ return SkScalarAbs(sweepAngle) <= 180.f;
+ }
+ // When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped
+ // to a secant, i.e. convex.
+ return SkScalarAbs(sweepAngle) <= 360.f;
+}
+
void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
SkASSERT(!oval.isEmpty());
@@ -3353,11 +3386,15 @@ void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar st
path->setFillType(SkPath::kWinding_FillType);
if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
path->addOval(oval);
+ SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect));
return;
}
if (useCenter) {
path->moveTo(oval.centerX(), oval.centerY());
}
+ auto firstDir =
+ sweepAngle > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
+ bool convex = DrawArcIsConvex(sweepAngle, useCenter, isFillNoPathEffect);
// Arc to mods at 360 and drawArc is not supposed to.
bool forceMoveTo = !useCenter;
while (sweepAngle <= -360.f) {
@@ -3380,6 +3417,8 @@ void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar st
if (useCenter) {
path->close();
}
+ path->setConvexity(convex ? SkPath::kConvex_Convexity : SkPath::kConcave_Convexity);
+ path->fFirstDirection.store(firstDir);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkPathPriv.h b/src/core/SkPathPriv.h
index ede79f60d9..3ee1f83d06 100644
--- a/src/core/SkPathPriv.h
+++ b/src/core/SkPathPriv.h
@@ -106,6 +106,12 @@ public:
SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect);
/**
+ * Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty
+ * oval.
+ */
+ static bool DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect);
+
+ /**
* Returns a C++11-iterable object that traverses a path's verbs in order. e.g:
*
* for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index bb541229d7..ef34a99385 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1353,10 +1353,9 @@ void GrRenderTargetContext::drawArc(const GrClip& clip,
return;
}
}
- SkPath path;
- SkPathPriv::CreateDrawArcPath(&path, oval, startAngle, sweepAngle, useCenter,
- style.isSimpleFill());
- this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, GrShape(path, style));
+ this->drawShapeUsingPathRenderer(
+ clip, std::move(paint), aa, viewMatrix,
+ GrShape::MakeArc(oval, startAngle, sweepAngle, useCenter, style));
}
void GrRenderTargetContext::drawImageLattice(const GrClip& clip,
diff --git a/src/gpu/GrShape.cpp b/src/gpu/GrShape.cpp
index 2d2bccbc0f..189b57be80 100644
--- a/src/gpu/GrShape.cpp
+++ b/src/gpu/GrShape.cpp
@@ -18,6 +18,9 @@ GrShape& GrShape::operator=(const GrShape& that) {
case Type::kRRect:
fRRectData = that.fRRectData;
break;
+ case Type::kArc:
+ fArcData = that.fArcData;
+ break;
case Type::kLine:
fLineData = that.fLineData;
break;
@@ -82,6 +85,14 @@ GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) {
result.fRRectData.fStart = kDefaultRRectStart;
result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion);
break;
+ case Type::kArc:
+ result.fType = original.fType;
+ result.fArcData.fOval = original.fArcData.fOval;
+ result.fArcData.fStartAngleDegrees = original.fArcData.fStartAngleDegrees;
+ result.fArcData.fSweepAngleDegrees = original.fArcData.fSweepAngleDegrees;
+ result.fArcData.fUseCenter = original.fArcData.fUseCenter;
+ result.fArcData.fInverted = is_inverted(original.fArcData.fInverted, inversion);
+ break;
case Type::kLine:
// Lines don't fill.
if (is_inverted(original.fLineData.fInverted, inversion)) {
@@ -144,6 +155,9 @@ SkRect GrShape::bounds() const {
}
case Type::kRRect:
return fRRectData.fRRect.getBounds();
+ case Type::kArc:
+ // Could make this less conservative by looking at angles.
+ return fArcData.fOval;
case Type::kPath:
return this->path().getBounds();
}
@@ -215,9 +229,13 @@ int GrShape::unstyledKeySize() const {
return 1;
case Type::kRRect:
SkASSERT(!fInheritedKey.count());
- SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
+ GR_STATIC_ASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
// + 1 for the direction, start index, and inverseness.
return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
+ case Type::kArc:
+ SkASSERT(!fInheritedKey.count());
+ GR_STATIC_ASSERT(0 == sizeof(fArcData) % sizeof(uint32_t));
+ return sizeof(fArcData) / sizeof(uint32_t);
case Type::kLine:
GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint));
// 4 for the end points and 1 for the inverseness
@@ -260,6 +278,10 @@ void GrShape::writeUnstyledKey(uint32_t* key) const {
*key++ |= fRRectData.fStart;
SkASSERT(fRRectData.fStart < 8);
break;
+ case Type::kArc:
+ memcpy(key, &fArcData, sizeof(fArcData));
+ key += sizeof(fArcData) / sizeof(uint32_t);
+ break;
case Type::kLine:
memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
key += 4;
@@ -349,6 +371,29 @@ void GrShape::addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) c
}
}
+GrShape GrShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees,
+ bool useCenter, const GrStyle& style) {
+#ifdef SK_DISABLE_ARC_TO_LINE_TO_CHECK
+ // When this flag is set the segment mask of the path won't match GrShape's segment mask for
+ // paths. Represent this shape as a path.
+ SkPath path;
+ SkPathPriv::CreateDrawArcPath(&path, oval, startAngleDegrees, sweepAngleDegrees, useCenter,
+ style.isSimpleFill());
+ return GrShape(path, style);
+#else
+ GrShape result;
+ result.changeType(Type::kArc);
+ result.fArcData.fOval = oval;
+ result.fArcData.fStartAngleDegrees = startAngleDegrees;
+ result.fArcData.fSweepAngleDegrees = sweepAngleDegrees;
+ result.fArcData.fUseCenter = useCenter;
+ result.fArcData.fInverted = false;
+ result.fStyle = style;
+ result.attemptToSimplifyArc();
+ return result;
+#endif
+}
+
GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
this->initType(that.fType, thatPath);
@@ -360,6 +405,9 @@ GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
case Type::kRRect:
fRRectData = that.fRRectData;
break;
+ case Type::kArc:
+ fArcData = that.fArcData;
+ break;
case Type::kLine:
fLineData = that.fLineData;
break;
@@ -588,6 +636,7 @@ void GrShape::attemptToSimplifyRRect() {
} else if (fStyle.isDashed()) {
// Dashing ignores the inverseness (currently). skbug.com/5421
fRRectData.fInverted = false;
+ // Possible TODO here: Check whether the dash results in a single arc or line.
}
// Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
if (!fStyle.hasPathEffect() &&
@@ -640,6 +689,46 @@ void GrShape::attemptToSimplifyLine() {
}
}
+void GrShape::attemptToSimplifyArc() {
+ SkASSERT(fType == Type::kArc);
+ SkASSERT(!fArcData.fInverted);
+ if (fArcData.fOval.isEmpty() || !fArcData.fSweepAngleDegrees) {
+ this->changeType(Type::kEmpty);
+ return;
+ }
+
+ // Assuming no path effect, a filled, stroked, hairline, or stroke-and-filled arc that traverses
+ // the full circle and doesn't use the center point is an oval. Unless it has square or round
+ // caps. They may protrude out of the oval. Round caps can't protrude out of a circle but we're
+ // ignoring that for now.
+ if (fStyle.isSimpleFill() || (!fStyle.pathEffect() && !fArcData.fUseCenter &&
+ fStyle.strokeRec().getCap() == SkPaint::kButt_Cap)) {
+ if (fArcData.fSweepAngleDegrees >= 360.f || fArcData.fSweepAngleDegrees <= -360.f) {
+ auto oval = fArcData.fOval;
+ this->changeType(Type::kRRect);
+ this->fRRectData.fRRect.setOval(oval);
+ this->fRRectData.fDir = kDefaultRRectDir;
+ this->fRRectData.fStart = kDefaultRRectStart;
+ this->fRRectData.fInverted = false;
+ return;
+ }
+ }
+ if (!fStyle.pathEffect()) {
+ // Canonicalize the arc such that the start is always in [0, 360) and the sweep is always
+ // positive.
+ if (fArcData.fSweepAngleDegrees < 0) {
+ fArcData.fStartAngleDegrees = fArcData.fStartAngleDegrees + fArcData.fSweepAngleDegrees;
+ fArcData.fSweepAngleDegrees = -fArcData.fSweepAngleDegrees;
+ }
+ }
+ if (this->fArcData.fStartAngleDegrees < 0 || this->fArcData.fStartAngleDegrees >= 360.f) {
+ this->fArcData.fStartAngleDegrees = SkScalarMod(this->fArcData.fStartAngleDegrees, 360.f);
+ }
+ // Possible TODOs here: Look at whether dash pattern results in a single dash and convert to
+ // non-dashed stroke. Stroke and fill can be fill if circular and no path effect. Just stroke
+ // could as well if the stroke fills the center.
+}
+
bool GrShape::attemptToSimplifyStrokedLineToRRect() {
SkASSERT(Type::kLine == fType);
SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style);
diff --git a/src/gpu/GrShape.h b/src/gpu/GrShape.h
index 99579ba4c3..36402330ce 100644
--- a/src/gpu/GrShape.h
+++ b/src/gpu/GrShape.h
@@ -116,6 +116,9 @@ public:
this->attemptToSimplifyRRect();
}
+ static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
+ SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
+
GrShape(const GrShape&);
GrShape& operator=(const GrShape& that);
@@ -209,6 +212,16 @@ public:
out->setFillType(kDefaultPathFillType);
}
break;
+ case Type::kArc:
+ SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
+ fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
+ fStyle.isSimpleFill());
+ if (fArcData.fInverted) {
+ out->setFillType(kDefaultPathInverseFillType);
+ } else {
+ out->setFillType(kDefaultPathFillType);
+ }
+ break;
case Type::kLine:
out->reset();
out->moveTo(fLineData.fPts[0]);
@@ -256,6 +269,10 @@ public:
return true;
case Type::kRRect:
return true;
+ case Type::kArc:
+ return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
+ SkToBool(fArcData.fUseCenter),
+ fStyle.isSimpleFill());
case Type::kLine:
return true;
case Type::kPath:
@@ -282,6 +299,9 @@ public:
case Type::kRRect:
ret = fRRectData.fInverted;
break;
+ case Type::kArc:
+ ret = fArcData.fInverted;
+ break;
case Type::kLine:
ret = fLineData.fInverted;
break;
@@ -320,6 +340,8 @@ public:
return true;
case Type::kRRect:
return true;
+ case Type::kArc:
+ return fArcData.fUseCenter;
case Type::kLine:
return false;
case Type::kPath:
@@ -343,6 +365,11 @@ public:
return SkPath::kLine_SegmentMask;
}
return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
+ case Type::kArc:
+ if (fArcData.fUseCenter) {
+ return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
+ }
+ return SkPath::kConic_SegmentMask;
case Type::kLine:
return SkPath::kLine_SegmentMask;
case Type::kPath:
@@ -387,6 +414,7 @@ private:
kEmpty,
kInvertedEmpty,
kRRect,
+ kArc,
kLine,
kPath,
};
@@ -438,6 +466,7 @@ private:
void attemptToSimplifyPath();
void attemptToSimplifyRRect();
void attemptToSimplifyLine();
+ void attemptToSimplifyArc();
bool attemptToSimplifyStrokedLineToRRect();
@@ -494,26 +523,33 @@ private:
return kPathRRectStartIdx;
}
- Type fType;
union {
struct {
- SkRRect fRRect;
- SkPath::Direction fDir;
- unsigned fStart;
- bool fInverted;
+ SkRRect fRRect;
+ SkPath::Direction fDir;
+ unsigned fStart;
+ bool fInverted;
} fRRectData;
struct {
- SkPath fPath;
+ SkRect fOval;
+ SkScalar fStartAngleDegrees;
+ SkScalar fSweepAngleDegrees;
+ int16_t fUseCenter;
+ int16_t fInverted;
+ } fArcData;
+ struct {
+ SkPath fPath;
// Gen ID of the original path (fPath may be modified)
- int32_t fGenID;
+ int32_t fGenID;
} fPathData;
struct {
- SkPoint fPts[2];
- bool fInverted;
+ SkPoint fPts[2];
+ bool fInverted;
} fLineData;
};
- GrStyle fStyle;
- SkTLazy<SkPath> fInheritedPathForListeners;
+ GrStyle fStyle;
+ SkTLazy<SkPath> fInheritedPathForListeners;
SkAutoSTArray<8, uint32_t> fInheritedKey;
+ Type fType;
};
#endif
diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp
index e8ec08785f..a53dad8517 100644
--- a/tests/GrShapeTest.cpp
+++ b/tests/GrShapeTest.cpp
@@ -60,7 +60,7 @@ static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds)
// everything got clipped out.
static constexpr int kRes = 2000;
// This tolerance is in units of 1/kRes fractions of the bounds width/height.
- static constexpr int kTol = 0;
+ static constexpr int kTol = 2;
GR_STATIC_ASSERT(kRes % 4 == 0);
SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
@@ -389,6 +389,34 @@ private:
SkRRect fRRect;
};
+class ArcGeo : public Geo {
+public:
+ ArcGeo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter)
+ : fOval(oval)
+ , fStartAngle(startAngle)
+ , fSweepAngle(sweepAngle)
+ , fUseCenter(useCenter) {}
+
+ SkPath path() const override {
+ SkPath path;
+ SkPathPriv::CreateDrawArcPath(&path, fOval, fStartAngle, fSweepAngle, fUseCenter, false);
+ return path;
+ }
+
+ GrShape makeShape(const SkPaint& paint) const override {
+ return GrShape::MakeArc(fOval, fStartAngle, fSweepAngle, fUseCenter, GrStyle(paint));
+ }
+
+ // GrShape specializes when created from arc params but it doesn't recognize arcs from SkPath.
+ bool isNonPath(const SkPaint& paint) const override { return false; }
+
+private:
+ SkRect fOval;
+ SkScalar fStartAngle;
+ SkScalar fSweepAngle;
+ bool fUseCenter;
+};
+
class PathGeo : public Geo {
public:
enum class Invert { kNo, kYes };
@@ -2121,6 +2149,10 @@ DEF_TEST(GrShape, reporter) {
PathGeo::Invert::kNo));
}
+ // Arcs
+ geos.emplace_back(new ArcGeo(SkRect::MakeWH(200, 100), 12.f, 110.f, false));
+ geos.emplace_back(new ArcGeo(SkRect::MakeWH(200, 100), 12.f, 110.f, true));
+
{
SkPath openRectPath;
openRectPath.moveTo(0, 0);
@@ -2221,4 +2253,80 @@ DEF_TEST(GrShape, reporter) {
test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo));
}
+DEF_TEST(GrShape_arcs, reporter) {
+ SkStrokeRec roundStroke(SkStrokeRec::kFill_InitStyle);
+ roundStroke.setStrokeStyle(2.f);
+ roundStroke.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 1.f);
+
+ SkStrokeRec squareStroke(roundStroke);
+ squareStroke.setStrokeParams(SkPaint::kSquare_Cap, SkPaint::kRound_Join, 1.f);
+
+ SkStrokeRec roundStrokeAndFill(roundStroke);
+ roundStrokeAndFill.setStrokeStyle(2.f, true);
+
+ static constexpr SkScalar kIntervals[] = {1, 2};
+ auto dash = SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), 1.5f);
+
+ SkTArray<GrStyle> styles;
+ styles.push_back(GrStyle::SimpleFill());
+ styles.push_back(GrStyle::SimpleHairline());
+ styles.push_back(GrStyle(roundStroke, nullptr));
+ styles.push_back(GrStyle(squareStroke, nullptr));
+ styles.push_back(GrStyle(roundStrokeAndFill, nullptr));
+ styles.push_back(GrStyle(roundStroke, dash));
+
+ for (const auto& style : styles) {
+ // An empty rect never draws anything according to SkCanvas::drawArc() docs.
+ TestCase emptyArc(GrShape::MakeArc(SkRect::MakeEmpty(), 0, 90.f, false, style), reporter);
+ TestCase emptyPath(reporter, SkPath(), style);
+ emptyArc.compare(reporter, emptyPath, TestCase::kAllSame_ComparisonExpecation);
+
+ static constexpr SkRect kOval1{0, 0, 50, 50};
+ static constexpr SkRect kOval2{50, 0, 100, 50};
+ // Test that swapping starting and ending angle doesn't change the shape unless the arc
+ // has a path effect. Also test that different ovals produce different shapes.
+ TestCase arc1CW(GrShape::MakeArc(kOval1, 0, 90.f, false, style), reporter);
+ TestCase arc1CCW(GrShape::MakeArc(kOval1, 90.f, -90.f, false, style), reporter);
+
+ TestCase arc1CWWithCenter(GrShape::MakeArc(kOval1, 0, 90.f, true, style), reporter);
+ TestCase arc1CCWWithCenter(GrShape::MakeArc(kOval1, 90.f, -90.f, true, style), reporter);
+
+ TestCase arc2CW(GrShape::MakeArc(kOval2, 0, 90.f, false, style), reporter);
+ TestCase arc2CWWithCenter(GrShape::MakeArc(kOval2, 0, 90.f, true, style), reporter);
+
+ auto reversedExepectations = style.hasPathEffect()
+ ? TestCase::kAllDifferent_ComparisonExpecation
+ : TestCase::kAllSame_ComparisonExpecation;
+ arc1CW.compare(reporter, arc1CCW, reversedExepectations);
+ arc1CWWithCenter.compare(reporter, arc1CCWWithCenter, reversedExepectations);
+ arc1CW.compare(reporter, arc2CW, TestCase::kAllDifferent_ComparisonExpecation);
+ arc1CW.compare(reporter, arc1CWWithCenter, TestCase::kAllDifferent_ComparisonExpecation);
+ arc1CWWithCenter.compare(reporter, arc2CWWithCenter,
+ TestCase::kAllDifferent_ComparisonExpecation);
+
+ // Test that two arcs that start at the same angle but specified differently are equivalent.
+ TestCase arc3A(GrShape::MakeArc(kOval1, 224.f, 73.f, false, style), reporter);
+ TestCase arc3B(GrShape::MakeArc(kOval1, 224.f - 360.f, 73.f, false, style), reporter);
+ arc3A.compare(reporter, arc3B, TestCase::kAllDifferent_ComparisonExpecation);
+
+ // Test that an arc that traverses the entire oval (and then some) is equivalent to the
+ // oval itself unless there is a path effect.
+ TestCase ovalArc(GrShape::MakeArc(kOval1, 150.f, -790.f, false, style), reporter);
+ TestCase oval(GrShape(SkRRect::MakeOval(kOval1)), reporter);
+ auto ovalExpectations = style.hasPathEffect() ? TestCase::kAllDifferent_ComparisonExpecation
+ : TestCase::kAllSame_ComparisonExpecation;
+ if (style.strokeRec().getWidth() >= 0 && style.strokeRec().getCap() != SkPaint::kButt_Cap) {
+ ovalExpectations = TestCase::kAllDifferent_ComparisonExpecation;
+ }
+ ovalArc.compare(reporter, oval, ovalExpectations);
+
+ // If the the arc starts/ends at the center then it is then equivalent to the oval only for
+ // simple fills.
+ TestCase ovalArcWithCenter(GrShape::MakeArc(kOval1, 304.f, 1225.f, true, style), reporter);
+ ovalExpectations = style.isSimpleFill() ? TestCase::kAllSame_ComparisonExpecation
+ : TestCase::kAllDifferent_ComparisonExpecation;
+ ovalArcWithCenter.compare(reporter, oval, ovalExpectations);
+ }
+}
+
#endif