diff options
-rw-r--r-- | gm/circulararcs.cpp | 85 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 13 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 7 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 13 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 39 | ||||
-rw-r--r-- | src/core/SkPathPriv.h | 7 | ||||
-rw-r--r-- | src/gpu/GrDrawContext.cpp | 10 | ||||
-rw-r--r-- | src/gpu/GrOvalRenderer.cpp | 5 |
8 files changed, 153 insertions, 26 deletions
diff --git a/gm/circulararcs.cpp b/gm/circulararcs.cpp index 290390a47f..148ee42169 100644 --- a/gm/circulararcs.cpp +++ b/gm/circulararcs.cpp @@ -7,6 +7,7 @@ #include <functional> #include "SkCanvas.h" +#include "SkDashPathEffect.h" #include "gm.h" static constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f}; @@ -125,3 +126,87 @@ DEF_ARC_GM(stroke_and_fill_round) { }; draw_arcs(canvas, setStroke); } + +DEF_SIMPLE_GM(circular_arcs_weird, canvas, 1000, 400) { + static constexpr SkScalar kS = 50; + struct Arc { + SkRect fOval; + SkScalar fStart; + SkScalar fSweep; + }; + static const Arc noDrawArcs[] = { + // no sweep + {SkRect::MakeWH(kS, kS), 0, 0}, + // empty rect in x + {SkRect::MakeWH(-kS, kS), 0, 90}, + // empty rect in y + {SkRect::MakeWH(kS, -kS), 0, 90}, + // empty rect in x and y + {SkRect::MakeWH( 0, 0), 0, 90}, + }; + static const Arc arcs[] = { + // large start + {SkRect::MakeWH(kS, kS), 810.f, 90.f}, + // large negative start + {SkRect::MakeWH(kS, kS), -810.f, 90.f}, + // exactly 360 sweep + {SkRect::MakeWH(kS, kS), 0.f, 360.f}, + // exactly -360 sweep + {SkRect::MakeWH(kS, kS), 0.f, -360.f}, + // exactly 540 sweep + {SkRect::MakeWH(kS, kS), 0.f, 540.f}, + // exactly -540 sweep + {SkRect::MakeWH(kS, kS), 0.f, -540.f}, + // generic large sweep and large start + {SkRect::MakeWH(kS, kS), 1125.f, 990.f}, + }; + SkTArray<SkPaint> paints; + // fill + paints.push_back(); + // stroke + paints.push_back().setStyle(SkPaint::kStroke_Style); + paints.back().setStrokeWidth(kS / 6.f); + // hairline + paints.push_back().setStyle(SkPaint::kStroke_Style); + paints.back().setStrokeWidth(0.f); + // stroke and fill + paints.push_back().setStyle(SkPaint::kStrokeAndFill_Style); + paints.back().setStrokeWidth(kS / 6.f); + // dash effect + paints.push_back().setStyle(SkPaint::kStroke_Style); + paints.back().setStrokeWidth(kS / 6.f); + static constexpr SkScalar kDashIntervals[] = {kS / 15, 2 * kS / 15}; + paints.back().setPathEffect(SkDashPathEffect::Make(kDashIntervals, 2, 0.f)); + + canvas->translate(kPad, kPad); + // This loop should draw nothing. + for (auto arc : noDrawArcs) { + for (auto paint : paints) { + paint.setAntiAlias(true); + canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint); + canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint); + } + } + + SkPaint linePaint; + linePaint.setAntiAlias(true); + linePaint.setColor(SK_ColorRED); + SkScalar midX = SK_ARRAY_COUNT(arcs) * (kS + kPad) - kPad/2.f; + SkScalar height = paints.count() * (kS + kPad); + canvas->drawLine(midX, -kPad, midX, height, linePaint); + + for (auto paint : paints) { + paint.setAntiAlias(true); + canvas->save(); + for (auto arc : arcs) { + canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint); + canvas->translate(kS + kPad, 0.f); + } + for (auto arc : arcs) { + canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint); + canvas->translate(kS + kPad, 0.f); + } + canvas->restore(); + canvas->translate(0, kS + kPad); + } +} diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 823c30fd7a..bc3b25dc1b 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -737,14 +737,17 @@ public: const SkPaint& paint); /** Draw the specified arc, which will be scaled to fit inside the - specified oval. If the sweep angle is >= 360, then the oval is drawn - completely. Note that this differs slightly from SkPath::arcTo, which - treats the sweep angle mod 360. + specified oval. Sweep angles are not treated as modulo 360 and thus can + exceed a full sweep of the oval. Note that this differs slightly from + SkPath::arcTo, which treats the sweep angle mod 360. If the oval is empty + or the sweep angle is zero nothing is drawn. If useCenter is true the oval + center is inserted into the implied path before the arc and the path is + closed back to the, center forming a wedge. Otherwise, the implied path + contains just the arc and is not closed. @param oval The bounds of oval used to define the shape of the arc. @param startAngle Starting angle (in degrees) where the arc begins @param sweepAngle Sweep angle (in degrees) measured clockwise. - @param useCenter true means include the center of the oval. For filling - this will draw a wedge. False means just use the arc. + @param useCenter true means include the center of the oval. @param paint The paint used to draw the arc */ void drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 0b14368b6e..8f9a576761 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -3098,11 +3098,10 @@ void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()"); - if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) { - this->drawOval(oval, paint); - } else { - this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint); + if (oval.isEmpty() || !sweepAngle) { + return; } + this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint); } void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength, diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index a90076d442..1f3bd613c1 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -16,6 +16,7 @@ #include "SkLatticeIter.h" #include "SkMetaData.h" #include "SkPatchUtils.h" +#include "SkPathPriv.h" #include "SkPathMeasure.h" #include "SkRasterClip.h" #include "SkRSXform.h" @@ -74,16 +75,10 @@ SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info void SkBaseDevice::drawArc(const SkDraw& draw, const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { - SkASSERT(SkScalarAbs(sweepAngle) >= 0.f && SkScalarAbs(sweepAngle) < 360.f); SkPath path; - if (useCenter) { - path.moveTo(oval.centerX(), oval.centerY()); - } - path.arcTo(oval, startAngle, sweepAngle, !useCenter); - if (useCenter) { - path.close(); - } - path.setIsVolatile(true); + bool isFillNoPathEffect = SkPaint::kFill_Style == paint.getStyle() && !paint.getPathEffect(); + SkPathPriv::CreateDrawArcPath(&path, oval, startAngle, sweepAngle, useCenter, + isFillNoPathEffect); this->drawPath(draw, path, paint); } diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index 64d3c0d36e..1fb3e11766 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -3341,3 +3341,42 @@ bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Di } return true; } + +void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle, + SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) { + SkASSERT(!oval.isEmpty()); + SkASSERT(sweepAngle); + + path->reset(); + path->setIsVolatile(true); + path->setFillType(SkPath::kWinding_FillType); + if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) { + path->addOval(oval); + return; + } + if (useCenter) { + path->moveTo(oval.centerX(), oval.centerY()); + } + // Arc to mods at 360 and drawArc is not supposed to. + bool forceMoveTo = !useCenter; + while (sweepAngle <= -360.f) { + path->arcTo(oval, startAngle, -180.f, forceMoveTo); + startAngle -= 180.f; + path->arcTo(oval, startAngle, -180.f, false); + startAngle -= 180.f; + forceMoveTo = false; + sweepAngle += 360.f; + } + while (sweepAngle >= 360.f) { + path->arcTo(oval, startAngle, 180.f, forceMoveTo); + startAngle += 180.f; + path->arcTo(oval, startAngle, 180.f, false); + startAngle += 180.f; + forceMoveTo = false; + sweepAngle -= 360.f; + } + path->arcTo(oval, startAngle, sweepAngle, forceMoveTo); + if (useCenter) { + path->close(); + } +} diff --git a/src/core/SkPathPriv.h b/src/core/SkPathPriv.h index 8689dee197..60b0f2a155 100644 --- a/src/core/SkPathPriv.h +++ b/src/core/SkPathPriv.h @@ -91,6 +91,13 @@ public: */ static bool IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction, unsigned* start); + + /** + * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function + * assumes empty ovals and zero sweeps have already been filtered out. + */ + static void CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle, + SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect); }; #endif diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp index 3b346b6efd..5bda884655 100644 --- a/src/gpu/GrDrawContext.cpp +++ b/src/gpu/GrDrawContext.cpp @@ -1023,14 +1023,8 @@ void GrDrawContext::drawArc(const GrClip& clip, } } SkPath path; - path.setIsVolatile(true); - if (useCenter) { - path.moveTo(oval.centerX(), oval.centerY()); - } - path.arcTo(oval, startAngle, sweepAngle, !useCenter); - if (useCenter) { - path.close(); - } + SkPathPriv::CreateDrawArcPath(&path, oval, startAngle, sweepAngle, useCenter, + style.isSimpleFill()); this->internalDrawPath(clip, paint, viewMatrix, path, style); return; } diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp index fe35869e2f..add634d33f 100644 --- a/src/gpu/GrOvalRenderer.cpp +++ b/src/gpu/GrOvalRenderer.cpp @@ -2027,7 +2027,12 @@ GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color, bool useCenter, const GrStyle& style, const GrShaderCaps* shaderCaps) { + SkASSERT(!oval.isEmpty()); + SkASSERT(sweepAngle); SkScalar width = oval.width(); + if (SkScalarAbs(sweepAngle) >= 360.f) { + return nullptr; + } if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) { return nullptr; } |