aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/circulararcs.cpp85
-rw-r--r--include/core/SkCanvas.h13
-rw-r--r--src/core/SkCanvas.cpp7
-rw-r--r--src/core/SkDevice.cpp13
-rw-r--r--src/core/SkPath.cpp39
-rw-r--r--src/core/SkPathPriv.h7
-rw-r--r--src/gpu/GrDrawContext.cpp10
-rw-r--r--src/gpu/GrOvalRenderer.cpp5
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;
}