Index: src/effects/SkDashPathEffect.cpp |
diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp |
index f9a56d0ff41fe129a6af8cb15c0dded767be211c..a053066191e955add6f6b8525a19ebc1c5c90b7e 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()) { |