aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2016-06-01 04:42:02 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-06-01 04:42:02 -0700
commitba150cc283301a28693a18d4aa9d14b1a1616ab3 (patch)
tree3c57abb22afd6821fc323185c5f914d14a35278d /src
parent276e63361c73fed6c6528b322400ece81fd1d067 (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.cpp65
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