Index: src/core/SkStroke.cpp |
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp |
index 6454f1694dd960c6018a00be8103e8d08798b215..b4af9185937c5c4609b6cfa5c132578b4eb5f0ce 100644 |
--- a/src/core/SkStroke.cpp |
+++ b/src/core/SkStroke.cpp |
@@ -231,16 +231,16 @@ private: |
bool fFoundTangents; // do less work until tangents meet (cubic) |
void addDegenerateLine(const SkQuadConstruct* ); |
- ReductionType CheckConicLinear(const SkConic& , SkPoint* reduction); |
- ReductionType CheckCubicLinear(const SkPoint cubic[4], SkPoint reduction[3], |
+ static ReductionType CheckConicLinear(const SkConic& , SkPoint* reduction); |
+ static ReductionType CheckCubicLinear(const SkPoint cubic[4], SkPoint reduction[3], |
const SkPoint** tanPtPtr); |
- ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction); |
- ResultType compareQuadConic(const SkConic& , SkQuadConstruct* ); |
+ static ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction); |
+ ResultType compareQuadConic(const SkConic& , SkQuadConstruct* ) const; |
ResultType compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* ); |
ResultType compareQuadQuad(const SkPoint quad[3], SkQuadConstruct* ); |
- bool conicPerpRay(const SkConic& , SkScalar t, SkPoint* tPt, SkPoint* onPt, |
+ void conicPerpRay(const SkConic& , SkScalar t, SkPoint* tPt, SkPoint* onPt, |
SkPoint* tangent) const; |
- bool conicQuadEnds(const SkConic& , SkQuadConstruct* ); |
+ 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, |
@@ -661,10 +661,20 @@ SkPathStroker::ReductionType SkPathStroker::CheckConicLinear(const SkConic& coni |
if (!conic_in_line(conic)) { |
return kQuad_ReductionType; |
} |
+#if 0 // once findMaxCurvature is implemented, this will be a better solution |
SkScalar t; |
if (!conic.findMaxCurvature(&t) || 0 == t) { |
return kLine_ReductionType; |
} |
+#else // but for now, use extrema instead |
+ SkScalar xT = 0, yT = 0; |
+ (void) conic.findXExtrema(&xT); |
+ (void) conic.findYExtrema(&yT); |
+ SkScalar t = SkTMax(xT, yT); |
+ if (0 == t) { |
+ return kLine_ReductionType; |
+ } |
+#endif |
conic.evalAt(t, reduction, NULL); |
return kDegenerate_ReductionType; |
} |
@@ -923,36 +933,30 @@ void SkPathStroker::setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, |
// Given a conic 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::conicPerpRay(const SkConic& conic, SkScalar t, SkPoint* tPt, SkPoint* onPt, |
+void SkPathStroker::conicPerpRay(const SkConic& conic, SkScalar t, SkPoint* tPt, SkPoint* onPt, |
SkPoint* tangent) const { |
SkVector dxy; |
conic.evalAt(t, tPt, &dxy); |
if (dxy.fX == 0 && dxy.fY == 0) { |
dxy = conic.fPts[2] - conic.fPts[0]; |
} |
- setRayPts(*tPt, &dxy, onPt, tangent); |
- return true; |
+ this->setRayPts(*tPt, &dxy, onPt, tangent); |
} |
// Given a conic and a t range, find the start and end if they haven't been found already. |
-bool SkPathStroker::conicQuadEnds(const SkConic& conic, SkQuadConstruct* quadPts) { |
+void SkPathStroker::conicQuadEnds(const SkConic& conic, SkQuadConstruct* quadPts) const { |
if (!quadPts->fStartSet) { |
SkPoint conicStartPt; |
- if (!this->conicPerpRay(conic, quadPts->fStartT, &conicStartPt, &quadPts->fQuad[0], |
- &quadPts->fTangentStart)) { |
- return false; |
- } |
+ this->conicPerpRay(conic, quadPts->fStartT, &conicStartPt, &quadPts->fQuad[0], |
+ &quadPts->fTangentStart); |
quadPts->fStartSet = true; |
} |
if (!quadPts->fEndSet) { |
SkPoint conicEndPt; |
- if (!this->conicPerpRay(conic, quadPts->fEndT, &conicEndPt, &quadPts->fQuad[2], |
- &quadPts->fTangentEnd)) { |
- return false; |
- } |
+ this->conicPerpRay(conic, quadPts->fEndT, &conicEndPt, &quadPts->fQuad[2], |
+ &quadPts->fTangentEnd); |
quadPts->fEndSet = true; |
} |
- return true; |
} |
@@ -1025,35 +1029,49 @@ SkPathStroker::ResultType SkPathStroker::intersectRay(SkQuadConstruct* quadPts, |
const SkPoint& end = quadPts->fQuad[2]; |
SkVector aLen = quadPts->fTangentStart - start; |
SkVector bLen = quadPts->fTangentEnd - end; |
+ /* Slopes match when denom goes to zero: |
+ axLen / ayLen == bxLen / byLen |
+ (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen |
+ byLen * axLen == ayLen * bxLen |
+ byLen * axLen - ayLen * bxLen ( == denom ) |
+ */ |
SkScalar denom = aLen.cross(bLen); |
+ if (denom == 0 || !SkScalarIsFinite(denom)) { |
+ return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, "denom == 0"); |
+ } |
SkVector ab0 = start - end; |
SkScalar numerA = bLen.cross(ab0); |
SkScalar numerB = aLen.cross(ab0); |
- if (!SkScalarNearlyZero(denom)) { |
+ if ((numerA >= 0) == (numerB >= 0)) { // if the control point is outside the quad ends |
// if the perpendicular distances from the quad points to the opposite tangent line |
// are small, a straight line is good enough |
SkScalar dist1 = pt_to_line(start, end, quadPts->fTangentEnd); |
SkScalar dist2 = pt_to_line(end, start, quadPts->fTangentStart); |
- if ((numerA >= 0) != (numerB >= 0)) { |
- if (kCtrlPt_RayType == intersectRayType) { |
- numerA /= denom; |
- SkPoint* ctrlPt = &quadPts->fQuad[1]; |
- ctrlPt->fX = start.fX * (1 - numerA) + quadPts->fTangentStart.fX * numerA; |
- ctrlPt->fY = start.fY * (1 - numerA) + quadPts->fTangentStart.fY * numerA; |
- } |
- return STROKER_RESULT(kQuad_ResultType, depth, quadPts, |
- "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB); |
- } |
if (SkTMax(dist1, dist2) <= fInvResScaleSquared) { |
return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, |
"SkTMax(dist1=%g, dist2=%g) <= fInvResScaleSquared", dist1, dist2); |
} |
return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
"(numerA=%g >= 0) == (numerB=%g >= 0)", numerA, numerB); |
- } else { // if the lines are parallel, straight line is good enough |
- return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, |
- "SkScalarNearlyZero(denom=%g)", denom); |
} |
+ // check to see if the denomerator is teeny relative to the numerator |
+ bool validDivide = SkScalarAbs(numerA) * SK_ScalarNearlyZero < SkScalarAbs(denom); |
+ SkASSERT(!SkScalarNearlyZero(denom / numerA) == validDivide); |
+ if (validDivide) { |
+ if (kCtrlPt_RayType == intersectRayType) { |
+ numerA /= denom; |
+ SkPoint* ctrlPt = &quadPts->fQuad[1]; |
+ // the intersection of the tangents need not be on the tangent segment |
+ // so 0 <= numerA <= 1 is not necessarily true |
+ ctrlPt->fX = start.fX * (1 - numerA) + quadPts->fTangentStart.fX * numerA; |
+ ctrlPt->fY = start.fY * (1 - numerA) + quadPts->fTangentStart.fY * numerA; |
+ } |
+ return STROKER_RESULT(kQuad_ResultType, depth, quadPts, |
+ "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB); |
+ } |
+ // if the lines are parallel, straight line is good enough |
+ return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, |
+ "SkScalarNearlyZero(denom=%g)", denom); |
} |
// Given a cubic and a t-range, determine if the stroke can be described by a quadratic. |
@@ -1062,7 +1080,7 @@ SkPathStroker::ResultType SkPathStroker::tangentsMeet(const SkPoint cubic[4], |
if (!this->cubicQuadEnds(cubic, quadPts)) { |
return kNormalError_ResultType; |
} |
- return intersectRay(quadPts, kResultType_RayType STROKER_DEBUG_PARAMS(fRecursionDepth)); |
+ return this->intersectRay(quadPts, kResultType_RayType STROKER_DEBUG_PARAMS(fRecursionDepth)); |
} |
// Intersect the line with the quad and return the t values on the quad where the line crosses. |
@@ -1178,7 +1196,7 @@ SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4] |
if (!this->cubicQuadEnds(cubic, quadPts)) { |
return kNormalError_ResultType; |
} |
- ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType |
+ ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType |
STROKER_DEBUG_PARAMS(fRecursionDepth) ); |
if (resultType != kQuad_ResultType) { |
return resultType; |
@@ -1188,26 +1206,24 @@ SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4] |
if (!this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], NULL)) { |
return kNormalError_ResultType; |
} |
- return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(fRecursionDepth)); |
+ return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts |
+ STROKER_DEBUG_PARAMS(fRecursionDepth)); |
} |
SkPathStroker::ResultType SkPathStroker::compareQuadConic(const SkConic& conic, |
- SkQuadConstruct* quadPts) { |
+ SkQuadConstruct* quadPts) const { |
// get the quadratic approximation of the stroke |
- if (!this->conicQuadEnds(conic, quadPts)) { |
- return kNormalError_ResultType; |
- } |
- ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType |
+ this->conicQuadEnds(conic, quadPts); |
+ ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType |
STROKER_DEBUG_PARAMS(fRecursionDepth) ); |
if (resultType != kQuad_ResultType) { |
return resultType; |
} |
// project a ray from the curve to the stroke |
SkPoint ray[2]; // points near midpoint on quad, midpoint on conic |
- if (!this->conicPerpRay(conic, quadPts->fMidT, &ray[1], &ray[0], NULL)) { |
- return kNormalError_ResultType; |
- } |
- return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(fRecursionDepth)); |
+ this->conicPerpRay(conic, quadPts->fMidT, &ray[1], &ray[0], NULL); |
+ return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts |
+ STROKER_DEBUG_PARAMS(fRecursionDepth)); |
} |
SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3], |
@@ -1225,7 +1241,7 @@ SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3], |
&quadPts->fTangentEnd); |
quadPts->fEndSet = true; |
} |
- ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType |
+ ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType |
STROKER_DEBUG_PARAMS(fRecursionDepth)); |
if (resultType != kQuad_ResultType) { |
return resultType; |
@@ -1233,7 +1249,8 @@ SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3], |
// project a ray from the curve to the stroke |
SkPoint ray[2]; |
this->quadPerpRay(quad, quadPts->fMidT, &ray[1], &ray[0], NULL); |
- return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(fRecursionDepth)); |
+ return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts |
+ STROKER_DEBUG_PARAMS(fRecursionDepth)); |
} |
void SkPathStroker::addDegenerateLine(const SkQuadConstruct* quadPts) { |
@@ -1577,8 +1594,8 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { |
} |
} |
- SkAutoConicToQuads converter; |
#ifdef SK_LEGACY_STROKE_CURVES |
+ SkAutoConicToQuads converter; |
const SkScalar conicTol = SK_Scalar1 / 4 / fResScale; |
#endif |
SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(), fResScale); |