Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(934)

Unified Diff: src/core/SkScan_Hairline.cpp

Issue 1491843006: add support for capped hairlines (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: add comment Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/core/SkScan.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
///////////////////////////////////////////////////////////////////////////////
« no previous file with comments | « src/core/SkScan.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698