diff options
author | Greg Daniel <egdaniel@google.com> | 2017-12-21 14:55:00 +0000 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-12-21 14:55:11 +0000 |
commit | 9838b49b2e756f16ed9d53cab825bf6530d7594b (patch) | |
tree | 9c4dc98c2004ebb39f76448e82c2186db048e315 | |
parent | 3eec5f322b51c2565d4a1c99b7b5fcb0eb91bf9c (diff) |
Revert "Revert "long rect dash fix with guards""
This reverts commit fa6b6c2d1b613faf72071b99f4b404fa4ee5e076.
Reason for revert: Looks like this was actually caused by another change but happen to fail a test that looked very related to this
Original change's description:
> Revert "long rect dash fix with guards"
>
> This reverts commit 93ceab1b59f06c828cf62aa6a700e7f81620f23d.
>
> Reason for revert: breaking layout test
>
> Original change's description:
> > long rect dash fix with guards
> >
> > long rect dash with guards
> >
> > check dash fix back in with
> > guards against changing
> > chrome layout test results
> >
> > original change clipped against wrong rectangle
> > some of the time, causing tiled drawing to fail.
> > Always clip against outset rectangle.
> >
> > original CL: skia-review.googlesource.com/c/skia/+/84862
> >
> > efficiently dash very large rectangles and very long lines
> > Speed up dashing when lines and rects are absurdly large.
> >
> > Prior to this CL, only horizontal lines were detected.
> >
> > Also folded in a change to handle dashing of zero length lines.
> >
> > TBR=egdaniel@google.com
> > Bug: skia:7311
> > Change-Id: Ic3c68ec8ea35d0597c892c3b26ba7bb077045990
> > Reviewed-on: https://skia-review.googlesource.com/87768
> > Reviewed-by: Cary Clark <caryclark@skia.org>
> > Commit-Queue: Cary Clark <caryclark@skia.org>
>
> TBR=egdaniel@google.com,caryclark@skia.org
>
> Change-Id: I56ef771ccb281887d7381c2bd8a2553acbd30621
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: skia:7311
> Reviewed-on: https://skia-review.googlesource.com/88421
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Greg Daniel <egdaniel@google.com>
TBR=egdaniel@google.com,caryclark@skia.org
Change-Id: Iecdd072544e6623bc4de8d5aab1402378112512d
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:7311
Reviewed-on: https://skia-review.googlesource.com/88424
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
-rw-r--r-- | gm/strokes.cpp | 30 | ||||
-rw-r--r-- | src/utils/SkDashPath.cpp | 131 |
2 files changed, 161 insertions, 0 deletions
diff --git a/gm/strokes.cpp b/gm/strokes.cpp index ed13d090ff..50b940a4ed 100644 --- a/gm/strokes.cpp +++ b/gm/strokes.cpp @@ -533,3 +533,33 @@ DEF_SIMPLE_GM(zerolinedash, canvas, 256, 256) { canvas->drawLine(100, 100, 100, 100, paint); } + +#ifdef PDF_IS_FIXED_SO_THIS_DOESNT_BREAK_IT +DEF_SIMPLE_GM(longrect_dash, canvas, 250, 250) { + canvas->clear(SK_ColorWHITE); + + SkPaint paint; + paint.setColor(SkColorSetARGB(255, 0, 0, 0)); + paint.setStrokeWidth(5); + paint.setStrokeCap(SkPaint::kRound_Cap); + paint.setStrokeJoin(SkPaint::kBevel_Join); + paint.setStyle(SkPaint::kStroke_Style); + SkScalar dash_pattern[] = {1, 5}; + paint.setPathEffect(SkDashPathEffect::Make(dash_pattern, 2, 0)); + // try all combinations of stretching bounds + for (auto left : { 20.f, -100001.f } ) { + for (auto top : { 20.f, -100001.f } ) { + for (auto right : { 40.f, 100001.f } ) { + for (auto bottom : { 40.f, 100001.f } ) { + canvas->save(); + canvas->clipRect({10, 10, 50, 50}); + canvas->drawRect({left, top, right, bottom}, paint); + canvas->restore(); + canvas->translate(60, 0); + } + } + canvas->translate(-60 * 4, 60); + } + } +} +#endif diff --git a/src/utils/SkDashPath.cpp b/src/utils/SkDashPath.cpp index cbfc8f2168..8d6c3cf567 100644 --- a/src/utils/SkDashPath.cpp +++ b/src/utils/SkDashPath.cpp @@ -83,11 +83,95 @@ static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) { rect->outset(radius, radius); } +#ifndef SK_SUPPORT_LEGACY_DASH_CULL_PATH +// If line is zero-length, bump out the end by a tiny amount +// to draw endcaps. The bump factor is sized so that +// SkPoint::Distance() computes a non-zero length. +// Offsets SK_ScalarNearlyZero or smaller create empty paths when Iter measures length. +// Large values are scaled by SK_ScalarNearlyZero so significant bits change. +static void adjust_zero_length_line(SkPoint pts[2]) { + SkASSERT(pts[0] == pts[1]); + pts[1].fX += SkTMax(1.001f, pts[1].fX) * SK_ScalarNearlyZero; +} + +static bool clip_line(SkPoint pts[2], const SkRect& bounds, SkScalar intervalLength, + SkScalar priorPhase) { + SkVector dxy = pts[1] - pts[0]; + + // only horizontal or vertical lines + if (dxy.fX && dxy.fY) { + return false; + } + int xyOffset = SkToBool(dxy.fY); // 0 to adjust horizontal, 1 to adjust vertical + + SkScalar minXY = (&pts[0].fX)[xyOffset]; + SkScalar maxXY = (&pts[1].fX)[xyOffset]; + bool swapped = maxXY < minXY; + if (swapped) { + SkTSwap(minXY, maxXY); + } + + SkASSERT(minXY <= maxXY); + SkScalar leftTop = (&bounds.fLeft)[xyOffset]; + SkScalar rightBottom = (&bounds.fRight)[xyOffset]; + if (maxXY < leftTop || minXY > rightBottom) { + return false; + } + + // Now we actually perform the chop, removing the excess to the left/top and + // right/bottom of the bounds (keeping our new line "in phase" with the dash, + // hence the (mod intervalLength). + + if (minXY < leftTop) { + minXY = leftTop - SkScalarMod(leftTop - minXY, intervalLength); + if (!swapped) { + minXY -= priorPhase; // for rectangles, adjust by prior phase + } + } + if (maxXY > rightBottom) { + maxXY = rightBottom + SkScalarMod(maxXY - rightBottom, intervalLength); + if (swapped) { + maxXY += priorPhase; // for rectangles, adjust by prior phase + } + } + + SkASSERT(maxXY >= minXY); + if (swapped) { + SkTSwap(minXY, maxXY); + } + (&pts[0].fX)[xyOffset] = minXY; + (&pts[1].fX)[xyOffset] = maxXY; + + if (minXY == maxXY) { + adjust_zero_length_line(pts); + } + return true; +} + +static bool contains_inclusive(const SkRect& rect, const SkPoint& pt) { + return rect.fLeft <= pt.fX && pt.fX <= rect.fRight && + rect.fTop <= pt.fY && pt.fY <= rect.fBottom; +} + +// Returns true is b is between a and c, that is: a <= b <= c, or a >= b >= c. +// Can perform this test with one branch by observing that, relative to b, +// the condition is true only if one side is positive and one side is negative. +// If the numbers are very small, the optimization may return the wrong result +// because the multiply may generate a zero where the simple compare does not. +// For this reason the assert does not fire when all three numbers are near zero. +static bool between(SkScalar a, SkScalar b, SkScalar c) { + SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0) + || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c))); + return (a - b) * (c - b) <= 0; +} +#endif + // Only handles lines for now. If returns true, dstPath is the new (smaller) // path. If returns false, then dstPath parameter is ignored. static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, const SkRect* cullRect, SkScalar intervalLength, SkPath* dstPath) { +#ifdef SK_SUPPORT_LEGACY_DASH_CULL_PATH if (nullptr == cullRect) { return false; } @@ -146,6 +230,53 @@ static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, if (minX == maxX) { pts[1].fX += maxX * FLT_EPSILON * 32; // 16 instead of 32 does not draw; length stays zero } +#else // !SK_SUPPORT_LEGACY_DASH_CULL_PATH + SkPoint pts[4]; + if (nullptr == cullRect) { + if (srcPath.isLine(pts) && pts[0] == pts[1]) { + adjust_zero_length_line(pts); + } else { + return false; + } + } else { + SkRect bounds; + bool isLine = srcPath.isLine(pts); + bool isRect = !isLine && srcPath.isRect(nullptr); + if (!isLine && !isRect) { + return false; + } + bounds = *cullRect; + outset_for_stroke(&bounds, rec); + if (isRect) { + // break rect into four lines, and call each one separately + SkPath::Iter iter(srcPath, false); + SkAssertResult(SkPath::kMove_Verb == iter.next(pts)); + SkScalar priorLength = 0; + while (SkPath::kLine_Verb == iter.next(pts)) { + SkVector v = pts[1] - pts[0]; + // if line is entirely outside clip rect, skip it + if (v.fX ? between(bounds.fTop, pts[0].fY, bounds.fBottom) : + between(bounds.fLeft, pts[0].fX, bounds.fRight)) { + bool skipMoveTo = contains_inclusive(bounds, pts[0]); + if (clip_line(pts, bounds, intervalLength, + SkScalarMod(priorLength, intervalLength))) { + if (0 == priorLength || !skipMoveTo) { + dstPath->moveTo(pts[0]); + } + dstPath->lineTo(pts[1]); + } + } + // keep track of all prior lengths to set phase of next line + priorLength += SkScalarAbs(v.fX ? v.fX : v.fY); + } + return !dstPath->isEmpty(); + } + SkASSERT(isLine); + if (!clip_line(pts, bounds, intervalLength, 0)) { + return false; + } + } +#endif dstPath->moveTo(pts[0]); dstPath->lineTo(pts[1]); return true; |