diff options
author | caryclark <caryclark@google.com> | 2016-06-01 04:42:02 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-06-01 04:42:02 -0700 |
commit | ba150cc283301a28693a18d4aa9d14b1a1616ab3 (patch) | |
tree | 3c57abb22afd6821fc323185c5f914d14a35278d /src | |
parent | 276e63361c73fed6c6528b322400ece81fd1d067 (diff) |
always compute a cubic tangent
If the inflection of a cubic is also the cusp, the tangent
is degenerate at that t when computed directly. Rather than
giving up, subdivide the curve there and use the computed
control points to compute the tangent.
This strategy also removes the error paths where the tangent
formerly could not be computed.
R=reed@google.com
BUG=615686
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2020293002
Review-Url: https://codereview.chromium.org/2020293002
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkStroke.cpp | 65 |
1 files changed, 26 insertions, 39 deletions
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp index 20bd286316..0f02a9449a 100644 --- a/src/core/SkStroke.cpp +++ b/src/core/SkStroke.cpp @@ -173,7 +173,6 @@ private: kSplit_ResultType, // the caller should split the quad stroke in two kDegenerate_ResultType, // the caller should add a line kQuad_ResultType, // the caller should (continue to try to) add a quad stroke - kNormalError_ResultType, // the cubic's normal couldn't be computed -- abort }; enum ReductionType { @@ -207,10 +206,10 @@ private: void conicQuadEnds(const SkConic& , SkQuadConstruct* ) const; bool conicStroke(const SkConic& , SkQuadConstruct* ); bool cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* ) const; - bool cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt, + void cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt, SkPoint* tangent) const; - bool cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* ); - bool cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* , SkPoint* mid) const; + void cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* ); + void cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* , SkPoint* mid) const; bool cubicStroke(const SkPoint cubic[4], SkQuadConstruct* ); void init(StrokeType strokeType, SkQuadConstruct* , SkScalar tStart, SkScalar tEnd); ResultType intersectRay(SkQuadConstruct* , IntersectRayType STROKER_DEBUG_PARAMS(int) ) const; @@ -786,52 +785,54 @@ void SkPathStroker::conicQuadEnds(const SkConic& conic, SkQuadConstruct* quadPts // Given a cubic and t, return the point on curve, its perpendicular, and the perpendicular tangent. -// Returns false if the perpendicular could not be computed (because the derivative collapsed to 0) -bool SkPathStroker::cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt, +void SkPathStroker::cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt, SkPoint* tangent) const { SkVector dxy; + SkPoint chopped[7]; SkEvalCubicAt(cubic, t, tPt, &dxy, nullptr); if (dxy.fX == 0 && dxy.fY == 0) { + const SkPoint* cPts = cubic; if (SkScalarNearlyZero(t)) { dxy = cubic[2] - cubic[0]; } else if (SkScalarNearlyZero(1 - t)) { dxy = cubic[3] - cubic[1]; } else { - return false; + // If the cubic inflection falls on the cusp, subdivide the cubic + // to find the tangent at that point. + SkChopCubicAt(cubic, chopped, t); + dxy = chopped[3] - chopped[2]; + if (dxy.fX == 0 && dxy.fY == 0) { + dxy = chopped[3] - chopped[1]; + cPts = chopped; + } } if (dxy.fX == 0 && dxy.fY == 0) { - dxy = cubic[3] - cubic[0]; + dxy = cPts[3] - cPts[0]; } } setRayPts(*tPt, &dxy, onPt, tangent); - return true; } // Given a cubic and a t range, find the start and end if they haven't been found already. -bool SkPathStroker::cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* quadPts) { +void SkPathStroker::cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* quadPts) { if (!quadPts->fStartSet) { SkPoint cubicStartPt; - if (!this->cubicPerpRay(cubic, quadPts->fStartT, &cubicStartPt, &quadPts->fQuad[0], - &quadPts->fTangentStart)) { - return false; - } + this->cubicPerpRay(cubic, quadPts->fStartT, &cubicStartPt, &quadPts->fQuad[0], + &quadPts->fTangentStart); quadPts->fStartSet = true; } if (!quadPts->fEndSet) { SkPoint cubicEndPt; - if (!this->cubicPerpRay(cubic, quadPts->fEndT, &cubicEndPt, &quadPts->fQuad[2], - &quadPts->fTangentEnd)) { - return false; - } + this->cubicPerpRay(cubic, quadPts->fEndT, &cubicEndPt, &quadPts->fQuad[2], + &quadPts->fTangentEnd); quadPts->fEndSet = true; } - return true; } -bool SkPathStroker::cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* quadPts, +void SkPathStroker::cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* quadPts, SkPoint* mid) const { SkPoint cubicMidPt; - return this->cubicPerpRay(cubic, quadPts->fMidT, &cubicMidPt, mid, nullptr); + this->cubicPerpRay(cubic, quadPts->fMidT, &cubicMidPt, mid, nullptr); } // Given a quad and t, return the point on curve, its perpendicular, and the perpendicular tangent. @@ -905,9 +906,7 @@ SkPathStroker::ResultType SkPathStroker::intersectRay(SkQuadConstruct* quadPts, // Given a cubic and a t-range, determine if the stroke can be described by a quadratic. SkPathStroker::ResultType SkPathStroker::tangentsMeet(const SkPoint cubic[4], SkQuadConstruct* quadPts) { - if (!this->cubicQuadEnds(cubic, quadPts)) { - return kNormalError_ResultType; - } + this->cubicQuadEnds(cubic, quadPts); return this->intersectRay(quadPts, kResultType_RayType STROKER_DEBUG_PARAMS(fRecursionDepth)); } @@ -1019,9 +1018,7 @@ SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[ SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* quadPts) { // get the quadratic approximation of the stroke - if (!this->cubicQuadEnds(cubic, quadPts)) { - return kNormalError_ResultType; - } + this->cubicQuadEnds(cubic, quadPts); ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType STROKER_DEBUG_PARAMS(fRecursionDepth) ); if (resultType != kQuad_ResultType) { @@ -1029,9 +1026,7 @@ SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4] } // project a ray from the curve to the stroke SkPoint ray[2]; // points near midpoint on quad, midpoint on cubic - if (!this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], nullptr)) { - return kNormalError_ResultType; - } + this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], nullptr); return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(fRecursionDepth)); } @@ -1087,9 +1082,7 @@ void SkPathStroker::addDegenerateLine(const SkQuadConstruct* quadPts) { bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* quadPts) const { SkPoint strokeMid; - if (!cubicQuadMid(cubic, quadPts, &strokeMid)) { - return false; - } + this->cubicQuadMid(cubic, quadPts, &strokeMid); SkScalar dist = pt_to_line(strokeMid, quadPts->fQuad[0], quadPts->fQuad[2]); return dist < fInvResScaleSquared; } @@ -1098,9 +1091,6 @@ bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts if (!fFoundTangents) { ResultType resultType = this->tangentsMeet(cubic, quadPts); if (kQuad_ResultType != resultType) { - if (kNormalError_ResultType == resultType) { - return false; - } if ((kDegenerate_ResultType == resultType || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2], fInvResScale)) && cubicMidOnLine(cubic, quadPts)) { @@ -1125,9 +1115,6 @@ bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts return true; } } - if (kNormalError_ResultType == resultType) { - return false; - } } if (!SkScalarIsFinite(quadPts->fQuad[2].fX) || !SkScalarIsFinite(quadPts->fQuad[2].fY)) { return false; // just abort if projected quad isn't representable |