aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar bsalomon <bsalomon@google.com>2016-08-25 12:29:23 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-08-25 12:29:24 -0700
commit21af9ca1b1f54d9ba1de055aa8475928d5c8ecdf (patch)
tree2d1d5dae4e72fd6066aff3ce812cd387922c5baa
parent29b2563afb1677515739f1d24fb27733626eca92 (diff)
Respecify SkCanvas::drawArc, consolidate conversion to SkPath, add GM for oddball drawArcs
Allows the arc to wind more than 360 degrees when useCenter is true, specs that nothing draws if the oval is empty or the sweep angle is 0. GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2281653002 Review-Url: https://codereview.chromium.org/2281653002
-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;
}