Index: src/core/SkScan_Hairline.cpp |
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp |
index 7a6e3ba1874e9c9c5ccd467e3ded73243d9b4ebe..7b344cd763ce252ce5063cb1da0e238b0cadac18 100644 |
--- a/src/core/SkScan_Hairline.cpp |
+++ b/src/core/SkScan_Hairline.cpp |
@@ -348,8 +348,65 @@ static int compute_quad_level(const SkPoint pts[3]) { |
return level; |
} |
+static void hairconic(SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, |
+ SkScalar weight, SkScan::HairRgnProc lineproc) { |
+ SkAutoConicToQuads converter; |
+ // how close should the quads be to the original conic? |
+ const SkScalar tol = SK_Scalar1 / 4; |
+ const SkPoint* quadPts = converter.computeQuads(pts, weight, tol); |
+ for (int i = 0; i < converter.countQuads(); ++i) { |
+ int level = compute_quad_level(quadPts); |
+ hairquad(quadPts, clip, blitter, level, lineproc); |
+ quadPts += 2; |
+ } |
+} |
+ |
+/* 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. */ |
+static void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) { |
+ 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 * 0.5f; |
+ first->fY += tangent.fY * 0.5f; |
+ } |
+ 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 * 0.5f; |
+ last->fY += tangent.fY * 0.5f; |
+ } |
+} |
+ |
+enum ExtendEnds { |
+ kNo_ExtendEnds, |
+ kCaps_ExtendEnds, |
+}; |
+ |
static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter, |
- SkScan::HairRgnProc lineproc) { |
+ SkScan::HairRgnProc lineproc, ExtendEnds extendForCaps) { |
if (path.isEmpty()) { |
return; |
} |
@@ -377,47 +434,92 @@ static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* |
SkPath::Iter iter(path, false); |
SkPoint pts[4]; |
SkPath::Verb verb; |
- SkAutoConicToQuads converter; |
- while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { |
- switch (verb) { |
- case SkPath::kMove_Verb: |
- break; |
- case SkPath::kLine_Verb: |
- lineproc(pts, 2, clip, blitter); |
- break; |
- case SkPath::kQuad_Verb: |
- hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); |
- break; |
- case SkPath::kConic_Verb: { |
- // how close should the quads be to the original conic? |
- const SkScalar tol = SK_Scalar1 / 4; |
- const SkPoint* quadPts = converter.computeQuads(pts, |
- iter.conicWeight(), tol); |
- for (int i = 0; i < converter.countQuads(); ++i) { |
- int level = compute_quad_level(quadPts); |
- hairquad(quadPts, clip, blitter, level, lineproc); |
- quadPts += 2; |
- } |
+ if (kNo_ExtendEnds == extendForCaps) { |
+ while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { |
+ switch (verb) { |
+ case SkPath::kMove_Verb: |
+ break; |
+ case SkPath::kLine_Verb: |
+ lineproc(pts, 2, clip, blitter); |
+ break; |
+ case SkPath::kQuad_Verb: |
+ hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); |
+ break; |
+ case SkPath::kConic_Verb: |
+ hairconic(pts, clip, blitter, iter.conicWeight(), lineproc); |
+ break; |
+ case SkPath::kCubic_Verb: |
+ haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); |
break; |
+ case SkPath::kClose_Verb: |
+ break; |
+ case SkPath::kDone_Verb: |
+ break; |
} |
- case SkPath::kCubic_Verb: { |
- haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); |
- } break; |
- case SkPath::kClose_Verb: |
+ } |
+ } else { |
+ /* If the caps require extending the haircurve, use the previous and following verbs |
+ to determine if the curve is at the start or end of the contour. If the curve is |
+ preceeded by a move verb, or followed by a move verb or a done verb, extend the curve. */ |
+ SkASSERT(kCaps_ExtendEnds == extendForCaps); |
+ SkPath::Verb prevVerb; |
+ verb = SkPath::kDone_Verb; |
+ SkScalar weight SK_INIT_TO_AVOID_WARNING; |
+ do { |
+ SkPoint nextPts[4]; |
+ SkPath::Verb nextVerb = iter.next(nextPts, false); |
+ switch (verb) { |
+ case SkPath::kMove_Verb: |
+ break; |
+ case SkPath::kLine_Verb: |
+ extend_pts(prevVerb, nextVerb, pts, 2); |
+ lineproc(pts, 2, clip, blitter); |
+ break; |
+ case SkPath::kQuad_Verb: |
+ extend_pts(prevVerb, nextVerb, pts, 3); |
+ hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); |
+ break; |
+ case SkPath::kConic_Verb: |
+ extend_pts(prevVerb, nextVerb, pts, 3); |
+ hairconic(pts, clip, blitter, weight, lineproc); |
+ break; |
+ case SkPath::kCubic_Verb: |
+ extend_pts(prevVerb, nextVerb, pts, 4); |
+ haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); |
break; |
- case SkPath::kDone_Verb: |
+ case SkPath::kClose_Verb: |
+ break; |
+ case SkPath::kDone_Verb: |
+ break; |
+ } |
+ if (SkPath::kDone_Verb == nextVerb) { |
break; |
- } |
+ } |
+ if (SkPath::kConic_Verb == nextVerb) { |
+ weight = iter.conicWeight(); |
+ } |
+ memcpy(pts, nextPts, sizeof(pts)); |
+ prevVerb = verb; |
+ verb = nextVerb; |
+ } while (true); |
} |
} |
void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
- hair_path(path, clip, blitter, SkScan::HairLineRgn); |
+ hair_path(path, clip, blitter, SkScan::HairLineRgn, kNo_ExtendEnds); |
} |
void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
- hair_path(path, clip, blitter, SkScan::AntiHairLineRgn); |
+ hair_path(path, clip, blitter, SkScan::AntiHairLineRgn, kNo_ExtendEnds); |
+} |
+ |
+void SkScan::HairCapPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
+ hair_path(path, clip, blitter, SkScan::HairLineRgn, kCaps_ExtendEnds); |
+} |
+ |
+void SkScan::AntiHairCapPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { |
+ hair_path(path, clip, blitter, SkScan::AntiHairLineRgn, kCaps_ExtendEnds); |
} |
/////////////////////////////////////////////////////////////////////////////// |