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

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: fix warning 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
« gm/hairlines.cpp ('K') | « 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 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);
}
///////////////////////////////////////////////////////////////////////////////
« gm/hairlines.cpp ('K') | « src/core/SkScan.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698