diff options
author | 2014-11-04 13:33:50 -0800 | |
---|---|---|
committer | 2014-11-04 13:33:50 -0800 | |
commit | 9f2251c73ed6f417dd1057d487bf523e04488440 (patch) | |
tree | 8d2195fb4413dbd74b7cd7f6cd7c8a2c6c832f68 /src | |
parent | 0e66aaa503fd30b9860c0306ae20e8d7758b0fe6 (diff) |
Crop the fast path dashed lines to the cull rect
Without:
maxrss loops min median mean max stddev samples config bench
56M 1 13.3ms 13.6ms 13.6ms 14.2ms 2% Ooooo..... 8888 GM_dashing5_bw
56M 13 390us 417us 416us 459us 5% ooooO..o.o gpu GM_dashing5_bw
56M 1 13.4ms 13.9ms 14.1ms 15ms 3% Oooo..ooOo 8888 GM_dashing5_aa
56M 13 402us 421us 416us 425us 2% Ooo.ooOOOO gpu GM_dashing5_aa
With:
40M 1 1.53ms 1.54ms 1.54ms 1.55ms 0% oo.O...o.. 8888 GM_dashing5_bw
40M 12 407us 412us 415us 445us 3% ...Oo..... gpu GM_dashing5_bw
40M 1 1.7ms 1.7ms 1.7ms 1.72ms 0% o.O....... 8888 GM_dashing5_aa
43M 13 405us 409us 409us 415us 1% ooo.Ooo..o gpu GM_dashing5_aa
The GM images (including the new one) are the same with and without this CL.
BUG=428296
Review URL: https://codereview.chromium.org/699623003
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkDraw.cpp | 2 | ||||
-rw-r--r-- | src/effects/SkDashPathEffect.cpp | 123 | ||||
-rw-r--r-- | src/utils/SkDashPath.cpp | 9 |
3 files changed, 126 insertions, 8 deletions
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index 82fd84b8bc..b9c39fa8ce 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -460,7 +460,7 @@ bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint, return true; } if (paint.getStrokeCap() != SkPaint::kRound_Cap && - matrix->rectStaysRect() && SkCanvas::kPoints_PointMode == mode) { + matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) { SkScalar sx = matrix->get(SkMatrix::kMScaleX); SkScalar sy = matrix->get(SkMatrix::kMScaleY); if (SkScalarNearlyZero(sx - sy)) { diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp index f9a56d0ff4..a053066191 100644 --- a/src/effects/SkDashPathEffect.cpp +++ b/src/effects/SkDashPathEffect.cpp @@ -41,6 +41,116 @@ bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src, fInitialDashLength, fInitialDashIndex, fIntervalLength); } +static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) { + SkScalar radius = SkScalarHalf(rec.getWidth()); + if (0 == radius) { + radius = SK_Scalar1; // hairlines + } + if (SkPaint::kMiter_Join == rec.getJoin()) { + radius = SkScalarMul(radius, rec.getMiter()); + } + rect->outset(radius, radius); +} + +// Attempt to trim the line to minimally cover the cull rect (currently +// only works for horizontal and vertical lines). +// Return true if processing should continue; false otherwise. +static bool cull_line(SkPoint* pts, const SkStrokeRec& rec, + const SkMatrix& ctm, const SkRect* cullRect, + const SkScalar intervalLength) { + if (NULL == cullRect) { + SkASSERT(false); // Shouldn't ever occur in practice + return false; + } + + SkScalar dx = pts[1].x() - pts[0].x(); + SkScalar dy = pts[1].y() - pts[0].y(); + + if (dx && dy) { + return false; + } + + SkRect bounds = *cullRect; + outset_for_stroke(&bounds, rec); + + // cullRect is in device space while pts are in the local coordinate system + // defined by the ctm. We want our answer in the local coordinate system. + + SkASSERT(ctm.rectStaysRect()); + SkMatrix inv; + if (!ctm.invert(&inv)) { + return false; + } + + inv.mapRect(&bounds); + + if (dx) { + SkASSERT(dx && !dy); + SkScalar minX = pts[0].fX; + SkScalar maxX = pts[1].fX; + + if (dx < 0) { + SkTSwap(minX, maxX); + } + + SkASSERT(minX < maxX); + if (maxX < bounds.fLeft || minX > bounds.fRight) { + return false; + } + + // Now we actually perform the chop, removing the excess to the left and + // right of the bounds (keeping our new line "in phase" with the dash, + // hence the (mod intervalLength). + + if (minX < bounds.fLeft) { + minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX, intervalLength); + } + if (maxX > bounds.fRight) { + maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight, intervalLength); + } + + SkASSERT(maxX > minX); + if (dx < 0) { + SkTSwap(minX, maxX); + } + pts[0].fX = minX; + pts[1].fX = maxX; + } else { + SkASSERT(dy && !dx); + SkScalar minY = pts[0].fY; + SkScalar maxY = pts[1].fY; + + if (dy < 0) { + SkTSwap(minY, maxY); + } + + SkASSERT(minY < maxY); + if (maxY < bounds.fTop || minY > bounds.fBottom) { + return false; + } + + // Now we actually perform the chop, removing the excess to the top and + // bottom of the bounds (keeping our new line "in phase" with the dash, + // hence the (mod intervalLength). + + if (minY < bounds.fTop) { + minY = bounds.fTop - SkScalarMod(bounds.fTop - minY, intervalLength); + } + if (maxY > bounds.fBottom) { + maxY = bounds.fBottom + SkScalarMod(maxY - bounds.fBottom, intervalLength); + } + + SkASSERT(maxY > minY); + if (dy < 0) { + SkTSwap(minY, maxY); + } + pts[0].fY = minY; + pts[1].fY = maxY; + } + + return true; +} + // Currently asPoints is more restrictive then it needs to be. In the future // we need to: // allow kRound_Cap capping (could allow rotations in the matrix with this) @@ -83,7 +193,12 @@ bool SkDashPathEffect::asPoints(PointData* results, return false; } - SkScalar length = SkPoint::Distance(pts[1], pts[0]); + // See if the line can be limited to something plausible. + if (!cull_line(pts, rec, matrix, cullRect, fIntervalLength)) { + return false; + } + + SkScalar length = SkPoint::Distance(pts[1], pts[0]); SkVector tangent = pts[1] - pts[0]; if (tangent.isZero()) { @@ -94,9 +209,11 @@ bool SkDashPathEffect::asPoints(PointData* results, // TODO: make this test for horizontal & vertical lines more robust bool isXAxis = true; - if (SK_Scalar1 == tangent.fX || -SK_Scalar1 == tangent.fX) { + if (SkScalarNearlyEqual(SK_Scalar1, tangent.fX) || + SkScalarNearlyEqual(-SK_Scalar1, tangent.fX)) { results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth())); - } else if (SK_Scalar1 == tangent.fY || -SK_Scalar1 == tangent.fY) { + } else if (SkScalarNearlyEqual(SK_Scalar1, tangent.fY) || + SkScalarNearlyEqual(-SK_Scalar1, tangent.fY)) { results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0])); isXAxis = false; } else if (SkPaint::kRound_Cap != rec.getCap()) { diff --git a/src/utils/SkDashPath.cpp b/src/utils/SkDashPath.cpp index 3c4aef343d..02de98f1d6 100644 --- a/src/utils/SkDashPath.cpp +++ b/src/utils/SkDashPath.cpp @@ -115,14 +115,15 @@ static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, SkScalar minX = pts[0].fX; SkScalar maxX = pts[1].fX; - if (maxX < bounds.fLeft || minX > bounds.fRight) { - return false; - } - if (dx < 0) { SkTSwap(minX, maxX); } + SkASSERT(minX < maxX); + if (maxX < bounds.fLeft || minX > bounds.fRight) { + return false; + } + // Now we actually perform the chop, removing the excess to the left and // right of the bounds (keeping our new line "in phase" with the dash, // hence the (mod intervalLength). |