Index: src/core/SkScan_Hairline.cpp |
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp |
index 3917081571f3352219599a2e35acfb59edb6957f..52399c9d7844a6fb513653c0ca31bccfc4fbda4c 100644 |
--- a/src/core/SkScan_Hairline.cpp |
+++ b/src/core/SkScan_Hairline.cpp |
@@ -348,7 +348,51 @@ static int compute_quad_level(const SkPoint pts[3]) { |
return level; |
} |
-static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter, |
+/* Extend the points in the direction of the starting or ending tangent by 1/2 unit to |
+ account for a round or square cap. If there's no distance between the end point and |
+ the control point, use the next control point to create a tangent. If the curve |
+ is degenerate, move the cap out 1/2 unit horizontally. */ |
+template <SkPaint::Cap capStyle> |
+void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) { |
+ SkASSERT(SkPaint::kSquare_Cap == capStyle || SkPaint::kRound_Cap == capStyle); |
+ // The area of a circle is PI*R*R. For a unit circle, R=1/2, and the cap covers half of that. |
+ const SkScalar capOutset = SkPaint::kSquare_Cap == capStyle ? 0.5f : SK_ScalarPI / 8; |
+ if (SkPath::kMove_Verb == prevVerb) { |
+ SkPoint* first = pts; |
+ SkPoint* ctrl = first; |
+ int controls = ptCount - 1; |
+ SkVector tangent; |
+ do { |
+ tangent = *first - *++ctrl; |
+ } while (tangent.isZero() && --controls > 0); |
+ if (tangent.isZero()) { |
+ tangent.set(1, 0); |
+ } else { |
+ tangent.normalize(); |
+ } |
+ first->fX += tangent.fX * capOutset; |
+ first->fY += tangent.fY * capOutset; |
+ } |
+ if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb) { |
+ SkPoint* last = &pts[ptCount - 1]; |
+ SkPoint* ctrl = last; |
+ int controls = ptCount - 1; |
+ SkVector tangent; |
+ do { |
+ tangent = *last - *--ctrl; |
+ } while (tangent.isZero() && --controls > 0); |
+ if (tangent.isZero()) { |
+ tangent.set(-1, 0); |
+ } else { |
+ tangent.normalize(); |
+ } |
+ last->fX += tangent.fX * capOutset; |
+ last->fY += tangent.fY * capOutset; |
+ } |
+} |
+ |
+template <SkPaint::Cap capStyle> |
+void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter, |
SkScan::HairRgnProc lineproc) { |
if (path.isEmpty()) { |
return; |
@@ -376,23 +420,35 @@ static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* |
SkPath::RawIter iter(path); |
SkPoint pts[4], firstPt, lastPt; |
- SkPath::Verb verb; |
+ SkPath::Verb verb, prevVerb; |
SkAutoConicToQuads converter; |
+ if (SkPaint::kButt_Cap != capStyle) { |
+ prevVerb = SkPath::kDone_Verb; |
+ } |
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { |
switch (verb) { |
case SkPath::kMove_Verb: |
firstPt = lastPt = pts[0]; |
break; |
case SkPath::kLine_Verb: |
+ if (SkPaint::kButt_Cap != capStyle) { |
+ extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2); |
+ } |
lineproc(pts, 2, clip, blitter); |
lastPt = pts[1]; |
break; |
case SkPath::kQuad_Verb: |
+ if (SkPaint::kButt_Cap != capStyle) { |
+ extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3); |
+ } |
hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); |
lastPt = pts[2]; |
break; |
case SkPath::kConic_Verb: { |
+ if (SkPaint::kButt_Cap != capStyle) { |
+ extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3); |
+ } |
// how close should the quads be to the original conic? |
const SkScalar tol = SK_Scalar1 / 4; |
const SkPoint* quadPts = converter.computeQuads(pts, |
@@ -406,6 +462,9 @@ static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* |
break; |
} |
case SkPath::kCubic_Verb: { |
+ if (SkPaint::kButt_Cap != capStyle) { |
+ extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4); |
+ } |
haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); |
lastPt = pts[3]; |
} break; |
@@ -417,15 +476,34 @@ static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* |
case SkPath::kDone_Verb: |
break; |
} |
+ if (SkPaint::kButt_Cap != capStyle) { |
+ prevVerb = verb; |
+ } |
} |
} |
void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
- hair_path(path, clip, blitter, SkScan::HairLineRgn); |
+ hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn); |
} |
void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
- hair_path(path, clip, blitter, SkScan::AntiHairLineRgn); |
+ hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn); |
+} |
+ |
+void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
+ hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn); |
+} |
+ |
+void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
+ hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn); |
+} |
+ |
+void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
+ hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn); |
+} |
+ |
+void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
+ hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn); |
} |
/////////////////////////////////////////////////////////////////////////////// |