diff options
author | Brian Salomon <bsalomon@google.com> | 2018-04-26 15:22:04 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-04-26 19:50:23 +0000 |
commit | e4949406ebcc5d5d8f83c1cd96209763d278b1e2 (patch) | |
tree | 758eb7f1bc477b258435ac248d0392c41aa18506 | |
parent | 8f379d4d642d83fcf712d819ced716a9df01e234 (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.cpp | 45 | ||||
-rw-r--r-- | src/core/SkPathPriv.h | 6 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContext.cpp | 7 | ||||
-rw-r--r-- | src/gpu/GrShape.cpp | 91 | ||||
-rw-r--r-- | src/gpu/GrShape.h | 58 | ||||
-rw-r--r-- | tests/GrShapeTest.cpp | 110 |
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 |