diff options
author | robertphillips <robertphillips@google.com> | 2016-08-26 05:30:19 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-08-26 05:30:19 -0700 |
commit | ad2344693c70f13d5e4216df8458b4d907395bde (patch) | |
tree | 6e4ce1773716748290645e5e18eeb91b723bb5c3 | |
parent | cc319b95a58ae15e88c3c028b8726e7cab9121ff (diff) |
Ignore fill when stroke & filling convex line-only paths
This seems to work well for miter and bevel joins with the resulting stroke and fill path remaining convex. There seems to be an issue with round joins where the outer generated shell is usually not convex. Without this CL the resulting stroke & filled paths are always concave.
Perf-wise (on Windows):
convex-lineonly-paths-stroke-and-fill bench
(in ms) w/o w/CL %decrease
8888 2.88 2.01 30.2
gpu 4.4 1.38 68.6
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2275243003
Review-Url: https://codereview.chromium.org/2275243003
-rw-r--r-- | gm/convex_all_line_paths.cpp | 39 | ||||
-rw-r--r-- | src/core/SkStroke.cpp | 40 |
2 files changed, 65 insertions, 14 deletions
diff --git a/gm/convex_all_line_paths.cpp b/gm/convex_all_line_paths.cpp index eb4bdcfd05..a54c0e4a01 100644 --- a/gm/convex_all_line_paths.cpp +++ b/gm/convex_all_line_paths.cpp @@ -28,12 +28,17 @@ namespace skiagm { // paths class ConvexLineOnlyPathsGM : public GM { public: - ConvexLineOnlyPathsGM() { + ConvexLineOnlyPathsGM(bool doStrokeAndFill) : fDoStrokeAndFill(doStrokeAndFill) { this->setBGColor(0xFFFFFFFF); } protected: - SkString onShortName() override { return SkString("convex-lineonly-paths"); } + SkString onShortName() override { + if (fDoStrokeAndFill) { + return SkString("convex-lineonly-paths-stroke-and-fill"); + } + return SkString("convex-lineonly-paths"); + } SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); } bool runAsBench() const override { return true; } @@ -258,20 +263,35 @@ protected: if (offset->fX+path.getBounds().width() > kGMWidth) { offset->fX = 0; offset->fY += kMaxPathHeight; + if (fDoStrokeAndFill) { + offset->fX += kStrokeWidth / 2.0f; + offset->fY += kStrokeWidth / 2.0f; + } } center = { offset->fX + SkScalarHalf(path.getBounds().width()), offset->fY}; offset->fX += path.getBounds().width(); + if (fDoStrokeAndFill) { + offset->fX += kStrokeWidth; + } } const SkColor colors[2] = { SK_ColorBLACK, SK_ColorWHITE }; const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction }; const float scales[] = { 1.0f, 0.75f, 0.5f, 0.25f, 0.1f, 0.01f, 0.001f }; + const SkPaint::Join joins[3] = { SkPaint::kRound_Join, + SkPaint::kBevel_Join, + SkPaint::kMiter_Join }; SkPaint paint; paint.setAntiAlias(true); for (size_t i = 0; i < SK_ARRAY_COUNT(scales); ++i) { SkPath path = GetPath(index, (int) i, dirs[i%2]); + if (fDoStrokeAndFill) { + paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setStrokeJoin(joins[i%3]); + paint.setStrokeWidth(SkIntToScalar(kStrokeWidth)); + } canvas->save(); canvas->translate(center.fX, center.fY); @@ -285,6 +305,10 @@ protected: void onDraw(SkCanvas* canvas) override { // the right edge of the last drawn path SkPoint offset = { 0, SkScalarHalf(kMaxPathHeight) }; + if (fDoStrokeAndFill) { + offset.fX += kStrokeWidth / 2.0f; + offset.fY += kStrokeWidth / 2.0f; + } for (int i = 0; i < kNumPaths; ++i) { this->drawPath(canvas, i, &offset); @@ -296,6 +320,11 @@ protected: SkPaint p; p.setAntiAlias(true); + if (fDoStrokeAndFill) { + p.setStyle(SkPaint::kStrokeAndFill_Style); + p.setStrokeJoin(SkPaint::kMiter_Join); + p.setStrokeWidth(SkIntToScalar(kStrokeWidth)); + } SkPath p1; p1.moveTo(60.8522949f, 364.671021f); @@ -307,15 +336,19 @@ protected: } private: + static const int kStrokeWidth = 10; static const int kNumPaths = 20; static const int kMaxPathHeight = 100; static const int kGMWidth = 512; static const int kGMHeight = 512; + bool fDoStrokeAndFill; + typedef GM INHERITED; }; ////////////////////////////////////////////////////////////////////////////// -DEF_GM(return new ConvexLineOnlyPathsGM;) +DEF_GM(return new ConvexLineOnlyPathsGM(false);) +DEF_GM(return new ConvexLineOnlyPathsGM(true);) } diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp index df3e0f98e1..8ff7910bdc 100644 --- a/src/core/SkStroke.cpp +++ b/src/core/SkStroke.cpp @@ -55,7 +55,7 @@ static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, S SkScalar radius, SkVector* normal, SkVector* unitNormal) { if (!unitNormal->setNormalize((after.fX - before.fX) * scale, - (after.fY - before.fY) * scale)) { + (after.fY - before.fY) * scale)) { return false; } unitNormal->rotateCCW(); @@ -121,7 +121,8 @@ class SkPathStroker { public: SkPathStroker(const SkPath& src, SkScalar radius, SkScalar miterLimit, SkPaint::Cap, - SkPaint::Join, SkScalar resScale); + SkPaint::Join, SkScalar resScale, + bool canIgnoreCenter); bool hasOnlyMoveTo() const { return 0 == fSegmentCount; } SkPoint moveToPt() const { return fFirstPt; } @@ -157,6 +158,7 @@ private: SkPoint fFirstOuterPt; int fSegmentCount; bool fPrevIsLine; + bool fCanIgnoreCenter; SkStrokerPriv::CapProc fCapper; SkStrokerPriv::JoinProc fJoiner; @@ -294,11 +296,19 @@ void SkPathStroker::finishContour(bool close, bool currIsLine) { fFirstUnitNormal, fRadius, fInvMiterLimit, fPrevIsLine, currIsLine); fOuter.close(); - // now add fInner as its own contour - fInner.getLastPt(&pt); - fOuter.moveTo(pt.fX, pt.fY); - fOuter.reversePathTo(fInner); - fOuter.close(); + + if (fCanIgnoreCenter) { + if (!fOuter.getBounds().contains(fInner.getBounds())) { + SkASSERT(fInner.getBounds().contains(fOuter.getBounds())); + fInner.swap(fOuter); + } + } else { + // now add fInner as its own contour + fInner.getLastPt(&pt); + fOuter.moveTo(pt.fX, pt.fY); + fOuter.reversePathTo(fInner); + fOuter.close(); + } } else { // add caps to start and end // cap the end fInner.getLastPt(&pt); @@ -321,9 +331,11 @@ void SkPathStroker::finishContour(bool close, bool currIsLine) { SkPathStroker::SkPathStroker(const SkPath& src, SkScalar radius, SkScalar miterLimit, - SkPaint::Cap cap, SkPaint::Join join, SkScalar resScale) + SkPaint::Cap cap, SkPaint::Join join, SkScalar resScale, + bool canIgnoreCenter) : fRadius(radius) - , fResScale(resScale) { + , fResScale(resScale) + , fCanIgnoreCenter(canIgnoreCenter) { /* This is only used when join is miter_join, but we initialize it here so that it is always defined, to fis valgrind warnings. @@ -1366,7 +1378,13 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { } } - SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(), fResScale); + // We can always ignore centers for stroke and fill convex line-only paths + // TODO: remove the line-only restriction + bool ignoreCenter = fDoFill && (src.getSegmentMasks() == SkPath::kLine_SegmentMask) && + src.isLastContourClosed() && src.isConvex(); + + SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(), + fResScale, ignoreCenter); SkPath::Iter iter(src, false); SkPath::Verb lastSegment = SkPath::kMove_Verb; @@ -1420,7 +1438,7 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { DONE: stroker.done(dst, lastSegment == SkPath::kLine_Verb); - if (fDoFill) { + if (fDoFill && !ignoreCenter) { if (SkPathPriv::CheapIsFirstDirection(src, SkPathPriv::kCCW_FirstDirection)) { dst->reverseAddPath(src); } else { |