| 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);
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|