| Index: src/core/SkStroke.cpp
|
| diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
|
| index db76cafe35a94925ec19a93488be6cc8e79642ce..1a44a3a19f6654aaf8cf093e492df4db8ad5d6f7 100644
|
| --- a/src/core/SkStroke.cpp
|
| +++ b/src/core/SkStroke.cpp
|
| @@ -9,26 +9,26 @@
|
| #include "SkGeometry.h"
|
| #include "SkPath.h"
|
|
|
| -#if QUAD_STROKE_APPROXIMATION
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
|
|
| enum {
|
| kTangent_RecursiveLimit,
|
| kCubic_RecursiveLimit,
|
| + kConic_RecursiveLimit,
|
| kQuad_RecursiveLimit
|
| };
|
|
|
| // quads with extreme widths (e.g. (0,1) (1,6) (0,3) width=5e7) recurse to point of failure
|
| // largest seen for normal cubics : 5, 26
|
| // largest seen for normal quads : 11
|
| - static const int kRecursiveLimits[] = { 5*3, 26*3, 11*3 }; // 3x limits seen in practical tests
|
| + static const int kRecursiveLimits[] = { 5*3, 26*3, 11*3, 11*3 }; // 3x limits seen in practice
|
|
|
| + SK_COMPILE_ASSERT(0 == kTangent_RecursiveLimit, cubic_stroke_relies_on_tangent_equalling_zero);
|
| + SK_COMPILE_ASSERT(1 == kCubic_RecursiveLimit, cubic_stroke_relies_on_cubic_equalling_one);
|
| SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kRecursiveLimits) == kQuad_RecursiveLimit + 1,
|
| recursive_limits_mismatch);
|
|
|
| - #ifdef SK_DEBUG // enables tweaking these values at runtime from SampleApp
|
| - bool gDebugStrokerErrorSet = false;
|
| - SkScalar gDebugStrokerError;
|
| -
|
| + #ifdef SK_DEBUG
|
| int gMaxRecursion[SK_ARRAY_COUNT(kRecursiveLimits)] = { 0 };
|
| #endif
|
| #ifndef DEBUG_QUAD_STROKER
|
| @@ -51,14 +51,16 @@
|
|
|
| #endif
|
|
|
| +#ifndef SK_QUAD_STROKE_APPROXIMATION
|
| #define kMaxQuadSubdivide 5
|
| #define kMaxCubicSubdivide 7
|
| +#endif
|
|
|
| static inline bool degenerate_vector(const SkVector& v) {
|
| return !SkPoint::CanNormalize(v.fX, v.fY);
|
| }
|
|
|
| -#if !QUAD_STROKE_APPROXIMATION
|
| +#ifndef SK_QUAD_STROKE_APPROXIMATION
|
| static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
|
| /* root2/2 is a 45-degree angle
|
| make this constant bigger for more subdivisions (but not >= 1)
|
| @@ -111,7 +113,7 @@ static bool set_normal_unitnormal(const SkVector& vec,
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
| -#if QUAD_STROKE_APPROXIMATION
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
|
|
| struct SkQuadConstruct { // The state of the quad stroke under construction.
|
| SkPoint fQuad[3]; // the stroked quad parallel to the original curve
|
| @@ -156,19 +158,16 @@ struct SkQuadConstruct { // The state of the quad stroke under construction.
|
|
|
| class SkPathStroker {
|
| public:
|
| -#if QUAD_STROKE_APPROXIMATION
|
| - SkPathStroker(const SkPath& src,
|
| - SkScalar radius, SkScalar miterLimit, SkScalar error, SkPaint::Cap,
|
| - SkPaint::Join, SkScalar resScale);
|
| -#else
|
| SkPathStroker(const SkPath& src,
|
| SkScalar radius, SkScalar miterLimit, SkPaint::Cap,
|
| SkPaint::Join, SkScalar resScale);
|
| -#endif
|
|
|
| void moveTo(const SkPoint&);
|
| void lineTo(const SkPoint&);
|
| void quadTo(const SkPoint&, const SkPoint&);
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| + void conicTo(const SkPoint&, const SkPoint&, SkScalar weight);
|
| +#endif
|
| void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
|
| void close(bool isLine) { this->finishContour(true, isLine); }
|
|
|
| @@ -181,12 +180,13 @@ public:
|
| SkScalar getResScale() const { return fResScale; }
|
|
|
| private:
|
| -#if QUAD_STROKE_APPROXIMATION
|
| - SkScalar fError;
|
| -#endif
|
| SkScalar fRadius;
|
| SkScalar fInvMiterLimit;
|
| SkScalar fResScale;
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| + SkScalar fInvResScale;
|
| + SkScalar fInvResScaleSquared;
|
| +#endif
|
|
|
| SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
|
| SkPoint fFirstPt, fPrevPt; // on original path
|
| @@ -200,7 +200,7 @@ private:
|
| SkPath fInner, fOuter; // outer is our working answer, inner is temp
|
| SkPath fExtra; // added as extra complete contours
|
|
|
| -#if QUAD_STROKE_APPROXIMATION
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| enum StrokeType {
|
| kOuter_StrokeType = 1, // use sign-opposite values later to flip perpendicular axis
|
| kInner_StrokeType = -1
|
| @@ -231,11 +231,17 @@ 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],
|
| const SkPoint** tanPtPtr);
|
| ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction);
|
| + ResultType compareQuadConic(const SkConic& , SkQuadConstruct* );
|
| ResultType compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* );
|
| ResultType compareQuadQuad(const SkPoint quad[3], SkQuadConstruct* );
|
| + bool conicPerpRay(const SkConic& , SkScalar t, SkPoint* tPt, SkPoint* onPt,
|
| + SkPoint* tangent) const;
|
| + bool conicQuadEnds(const SkConic& , SkQuadConstruct* );
|
| + 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,
|
| SkPoint* tangent) const;
|
| @@ -248,6 +254,9 @@ private:
|
| void quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* onPt,
|
| SkPoint* tangent) const;
|
| bool quadStroke(const SkPoint quad[3], SkQuadConstruct* );
|
| + void setConicEndNormal(const SkConic& ,
|
| + const SkVector& normalAB, const SkVector& unitNormalAB,
|
| + SkVector* normalBC, SkVector* unitNormalBC);
|
| void setCubicEndNormal(const SkPoint cubic[4],
|
| const SkVector& normalAB, const SkVector& unitNormalAB,
|
| SkVector* normalCD, SkVector* unitNormalCD);
|
| @@ -268,7 +277,7 @@ private:
|
| const SkVector& unitNormal);
|
|
|
| void line_to(const SkPoint& currPt, const SkVector& normal);
|
| -#if !QUAD_STROKE_APPROXIMATION
|
| +#ifndef SK_QUAD_STROKE_APPROXIMATION
|
| void quad_to(const SkPoint pts[3],
|
| const SkVector& normalAB, const SkVector& unitNormalAB,
|
| SkVector* normalBC, SkVector* unitNormalBC,
|
| @@ -348,16 +357,11 @@ void SkPathStroker::finishContour(bool close, bool currIsLine) {
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| -#if QUAD_STROKE_APPROXIMATION
|
| -SkPathStroker::SkPathStroker(const SkPath& src,
|
| - SkScalar radius, SkScalar miterLimit, SkScalar error,
|
| - SkPaint::Cap cap, SkPaint::Join join, SkScalar resScale)
|
| -#else
|
| SkPathStroker::SkPathStroker(const SkPath& src,
|
| SkScalar radius, SkScalar miterLimit,
|
| SkPaint::Cap cap, SkPaint::Join join, SkScalar resScale)
|
| -#endif
|
| - : fRadius(radius), fResScale(resScale) {
|
| + : fRadius(radius)
|
| + , fResScale(resScale) {
|
|
|
| /* This is only used when join is miter_join, but we initialize it here
|
| so that it is always defined, to fis valgrind warnings.
|
| @@ -386,15 +390,11 @@ SkPathStroker::SkPathStroker(const SkPath& src,
|
| fOuter.setIsVolatile(true);
|
| fInner.incReserve(src.countPoints());
|
| fInner.setIsVolatile(true);
|
| -#if QUAD_STROKE_APPROXIMATION
|
| -#ifdef SK_DEBUG
|
| - if (!gDebugStrokerErrorSet) {
|
| - gDebugStrokerError = error;
|
| - }
|
| - fError = gDebugStrokerError;
|
| -#else
|
| - fError = error;
|
| -#endif
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| + // TODO : write a common error function used by stroking and filling
|
| + // The '4' below matches the fill scan converter's error term
|
| + fInvResScale = SkScalarInvert(resScale * 4);
|
| + fInvResScaleSquared = fInvResScale * fInvResScale;
|
| fRecursionDepth = 0;
|
| #endif
|
| }
|
| @@ -423,7 +423,7 @@ void SkPathStroker::lineTo(const SkPoint& currPt) {
|
| this->postJoinTo(currPt, normal, unitNormal);
|
| }
|
|
|
| -#if !QUAD_STROKE_APPROXIMATION
|
| +#ifndef SK_QUAD_STROKE_APPROXIMATION
|
| void SkPathStroker::quad_to(const SkPoint pts[3],
|
| const SkVector& normalAB, const SkVector& unitNormalAB,
|
| SkVector* normalBC, SkVector* unitNormalBC,
|
| @@ -461,7 +461,7 @@ void SkPathStroker::quad_to(const SkPoint pts[3],
|
| }
|
| #endif
|
|
|
| -#if QUAD_STROKE_APPROXIMATION
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| void SkPathStroker::setQuadEndNormal(const SkPoint quad[3], const SkVector& normalAB,
|
| const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC) {
|
| if (!set_normal_unitnormal(quad[1], quad[2], fRadius, normalBC, unitNormalBC)) {
|
| @@ -470,6 +470,11 @@ void SkPathStroker::setQuadEndNormal(const SkPoint quad[3], const SkVector& norm
|
| }
|
| }
|
|
|
| +void SkPathStroker::setConicEndNormal(const SkConic& conic, const SkVector& normalAB,
|
| + const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC) {
|
| + setQuadEndNormal(conic.fPts, normalAB, unitNormalAB, normalBC, unitNormalBC);
|
| +}
|
| +
|
| void SkPathStroker::setCubicEndNormal(const SkPoint cubic[4], const SkVector& normalAB,
|
| const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD) {
|
| SkVector ab = cubic[1] - cubic[0];
|
| @@ -547,7 +552,8 @@ static SkScalar pt_to_line(const SkPoint& pt, const SkPoint& lineStart, const Sk
|
| */
|
| static bool cubic_in_line(const SkPoint cubic[4]) {
|
| SkScalar ptMax = -1;
|
| - int outer1, outer2;
|
| + int outer1 SK_INIT_TO_AVOID_WARNING;
|
| + int outer2 SK_INIT_TO_AVOID_WARNING;
|
| for (int index = 0; index < 3; ++index) {
|
| for (int inner = index + 1; inner < 4; ++inner) {
|
| SkVector testDiff = cubic[inner] - cubic[index];
|
| @@ -583,7 +589,8 @@ static bool cubic_in_line(const SkPoint cubic[4]) {
|
| */
|
| static bool quad_in_line(const SkPoint quad[3]) {
|
| SkScalar ptMax = -1;
|
| - int outer1, outer2;
|
| + int outer1 SK_INIT_TO_AVOID_WARNING;
|
| + int outer2 SK_INIT_TO_AVOID_WARNING;
|
| for (int index = 0; index < 2; ++index) {
|
| for (int inner = index + 1; inner < 3; ++inner) {
|
| SkVector testDiff = quad[inner] - quad[index];
|
| @@ -603,6 +610,10 @@ static bool quad_in_line(const SkPoint quad[3]) {
|
| return pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= lineSlop;
|
| }
|
|
|
| +static bool conic_in_line(const SkConic& conic) {
|
| + return quad_in_line(conic.fPts);
|
| +}
|
| +
|
| SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(const SkPoint cubic[4],
|
| SkPoint reduction[3], const SkPoint** tangentPtPtr) {
|
| bool degenerateAB = degenerate_vector(cubic[1] - cubic[0]);
|
| @@ -634,6 +645,27 @@ SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(const SkPoint cubic
|
| return (ReductionType) (kQuad_ReductionType + count);
|
| }
|
|
|
| +SkPathStroker::ReductionType SkPathStroker::CheckConicLinear(const SkConic& conic,
|
| + SkPoint* reduction) {
|
| + bool degenerateAB = degenerate_vector(conic.fPts[1] - conic.fPts[0]);
|
| + bool degenerateBC = degenerate_vector(conic.fPts[2] - conic.fPts[1]);
|
| + if (degenerateAB & degenerateBC) {
|
| + return kPoint_ReductionType;
|
| + }
|
| + if (degenerateAB | degenerateBC) {
|
| + return kLine_ReductionType;
|
| + }
|
| + if (!conic_in_line(conic)) {
|
| + return kQuad_ReductionType;
|
| + }
|
| + SkScalar t;
|
| + if (!conic.findMaxCurvature(&t) || 0 == t) {
|
| + return kLine_ReductionType;
|
| + }
|
| + conic.evalAt(t, reduction, NULL);
|
| + return kDegenerate_ReductionType;
|
| +}
|
| +
|
| SkPathStroker::ReductionType SkPathStroker::CheckQuadLinear(const SkPoint quad[3],
|
| SkPoint* reduction) {
|
| bool degenerateAB = degenerate_vector(quad[1] - quad[0]);
|
| @@ -742,8 +774,45 @@ DRAW_LINE:
|
| }
|
| #endif
|
|
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| +void SkPathStroker::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight) {
|
| + const SkConic conic(fPrevPt, pt1, pt2, weight);
|
| + SkPoint reduction;
|
| + ReductionType reductionType = CheckConicLinear(conic, &reduction);
|
| + if (kPoint_ReductionType == reductionType) {
|
| + return;
|
| + }
|
| + if (kLine_ReductionType == reductionType) {
|
| + this->lineTo(pt2);
|
| + return;
|
| + }
|
| + if (kDegenerate_ReductionType == reductionType) {
|
| + this->lineTo(reduction);
|
| + SkStrokerPriv::JoinProc saveJoiner = fJoiner;
|
| + fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join);
|
| + this->lineTo(pt2);
|
| + fJoiner = saveJoiner;
|
| + return;
|
| + }
|
| + SkASSERT(kQuad_ReductionType == reductionType);
|
| + SkVector normalAB, unitAB, normalBC, unitBC;
|
| + this->preJoinTo(pt1, &normalAB, &unitAB, false);
|
| + SkQuadConstruct quadPts;
|
| + this->init(kOuter_StrokeType, &quadPts, 0, 1);
|
| + if (!this->conicStroke(conic, &quadPts)) {
|
| + return;
|
| + }
|
| + this->init(kInner_StrokeType, &quadPts, 0, 1);
|
| + if (!this->conicStroke(conic, &quadPts)) {
|
| + return;
|
| + }
|
| + this->setConicEndNormal(conic, normalAB, unitAB, &normalBC, &unitBC);
|
| + this->postJoinTo(pt2, normalBC, unitBC);
|
| +}
|
| +#endif
|
| +
|
| void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
|
| -#if QUAD_STROKE_APPROXIMATION
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| const SkPoint quad[3] = { fPrevPt, pt1, pt2 };
|
| SkPoint reduction;
|
| ReductionType reductionType = CheckQuadLinear(quad, &reduction);
|
| @@ -831,7 +900,7 @@ void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
|
| this->postJoinTo(pt2, normalBC, unitBC);
|
| }
|
|
|
| -#if QUAD_STROKE_APPROXIMATION
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| // Given a point on the curve and its derivative, scale the derivative by the radius, and
|
| // compute the perpendicular point and its tangent.
|
| void SkPathStroker::setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt,
|
| @@ -852,6 +921,41 @@ 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,
|
| + 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;
|
| +}
|
| +
|
| +// 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) {
|
| + if (!quadPts->fStartSet) {
|
| + SkPoint conicStartPt;
|
| + if (!this->conicPerpRay(conic, quadPts->fStartT, &conicStartPt, &quadPts->fQuad[0],
|
| + &quadPts->fTangentStart)) {
|
| + return false;
|
| + }
|
| + quadPts->fStartSet = true;
|
| + }
|
| + if (!quadPts->fEndSet) {
|
| + SkPoint conicEndPt;
|
| + if (!this->conicPerpRay(conic, quadPts->fEndT, &conicEndPt, &quadPts->fQuad[2],
|
| + &quadPts->fTangentEnd)) {
|
| + return false;
|
| + }
|
| + quadPts->fEndSet = true;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| // 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,
|
| @@ -930,10 +1034,6 @@ SkPathStroker::ResultType SkPathStroker::intersectRay(SkQuadConstruct* quadPts,
|
| // 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 (SkTMax(dist1, dist2) <= fError * fError) {
|
| - return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts,
|
| - "SkTMax(dist1=%g, dist2=%g) <= fError * fError", dist1, dist2);
|
| - }
|
| if ((numerA >= 0) != (numerB >= 0)) {
|
| if (kCtrlPt_RayType == intersectRayType) {
|
| numerA /= denom;
|
| @@ -944,6 +1044,10 @@ SkPathStroker::ResultType SkPathStroker::intersectRay(SkQuadConstruct* quadPts,
|
| 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
|
| @@ -979,19 +1083,19 @@ static int intersect_quad_ray(const SkPoint line[2], const SkPoint quad[3], SkSc
|
| // Return true if the point is close to the bounds of the quad. This is used as a quick reject.
|
| bool SkPathStroker::ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const {
|
| SkScalar xMin = SkTMin(SkTMin(quad[0].fX, quad[1].fX), quad[2].fX);
|
| - if (pt.fX + fError < xMin) {
|
| + if (pt.fX + fInvResScale < xMin) {
|
| return false;
|
| }
|
| SkScalar xMax = SkTMax(SkTMax(quad[0].fX, quad[1].fX), quad[2].fX);
|
| - if (pt.fX - fError > xMax) {
|
| + if (pt.fX - fInvResScale > xMax) {
|
| return false;
|
| }
|
| SkScalar yMin = SkTMin(SkTMin(quad[0].fY, quad[1].fY), quad[2].fY);
|
| - if (pt.fY + fError < yMin) {
|
| + if (pt.fY + fInvResScale < yMin) {
|
| return false;
|
| }
|
| SkScalar yMax = SkTMax(SkTMax(quad[0].fY, quad[1].fY), quad[2].fY);
|
| - if (pt.fY - fError > yMax) {
|
| + if (pt.fY - fInvResScale > yMax) {
|
| return false;
|
| }
|
| return true;
|
| @@ -1022,7 +1126,7 @@ SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[
|
| SkPoint strokeMid;
|
| SkEvalQuadAt(stroke, SK_ScalarHalf, &strokeMid);
|
| // measure the distance from the curve to the quad-stroke midpoint, compare to radius
|
| - if (points_within_dist(ray[0], strokeMid, fError)) { // if the difference is small
|
| + if (points_within_dist(ray[0], strokeMid, fInvResScaleSquared)) { // if the difference is small
|
| if (sharp_angle(quadPts->fQuad)) {
|
| return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
|
| "sharp_angle (1) =%g,%g, %g,%g, %g,%g",
|
| @@ -1031,8 +1135,8 @@ SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[
|
| quadPts->fQuad[2].fX, quadPts->fQuad[2].fY);
|
| }
|
| return STROKER_RESULT(kQuad_ResultType, depth, quadPts,
|
| - "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fError)",
|
| - ray[0].fX, ray[0].fY, strokeMid.fX, strokeMid.fY);
|
| + "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fInvResScaleSquared=%g)",
|
| + ray[0].fX, ray[0].fY, strokeMid.fX, strokeMid.fY, fInvResScaleSquared);
|
| }
|
| // measure the distance to quad's bounds (quick reject)
|
| // an alternative : look for point in triangle
|
| @@ -1051,7 +1155,7 @@ SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[
|
| }
|
| SkPoint quadPt;
|
| SkEvalQuadAt(stroke, roots[0], &quadPt);
|
| - SkScalar error = fError * (SK_Scalar1 - SkScalarAbs(roots[0] - 0.5f) * 2);
|
| + SkScalar error = fInvResScale * (SK_Scalar1 - SkScalarAbs(roots[0] - 0.5f) * 2);
|
| if (points_within_dist(ray[0], quadPt, error)) { // if the difference is small, we're done
|
| if (sharp_angle(quadPts->fQuad)) {
|
| return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
|
| @@ -1080,14 +1184,32 @@ SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4]
|
| return resultType;
|
| }
|
| // project a ray from the curve to the stroke
|
| - SkPoint ray[2]; // point near midpoint on quad, midpoint on cubic
|
| + SkPoint ray[2]; // points near midpoint on quad, midpoint on cubic
|
| if (!this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], NULL)) {
|
| return kNormalError_ResultType;
|
| }
|
| return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(fRecursionDepth));
|
| }
|
|
|
| -// if false is returned, caller splits quadratic approximation
|
| +SkPathStroker::ResultType SkPathStroker::compareQuadConic(const SkConic& conic,
|
| + SkQuadConstruct* quadPts) {
|
| + // get the quadratic approximation of the stroke
|
| + if (!this->conicQuadEnds(conic, quadPts)) {
|
| + return kNormalError_ResultType;
|
| + }
|
| + ResultType resultType = 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));
|
| +}
|
| +
|
| SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3],
|
| SkQuadConstruct* quadPts) {
|
| // get the quadratic approximation of the stroke
|
| @@ -1126,7 +1248,7 @@ bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct
|
| return false;
|
| }
|
| SkScalar dist = pt_to_line(strokeMid, quadPts->fQuad[0], quadPts->fQuad[2]);
|
| - return dist < fError * fError;
|
| + return dist < fInvResScaleSquared;
|
| }
|
|
|
| bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts) {
|
| @@ -1137,8 +1259,8 @@ bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts
|
| return false;
|
| }
|
| if ((kDegenerate_ResultType == resultType
|
| - || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2], fError))
|
| - && cubicMidOnLine(cubic, quadPts)) {
|
| + || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2],
|
| + fInvResScaleSquared)) && cubicMidOnLine(cubic, quadPts)) {
|
| addDegenerateLine(quadPts);
|
| return true;
|
| }
|
| @@ -1189,6 +1311,36 @@ bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts
|
| return true;
|
| }
|
|
|
| +bool SkPathStroker::conicStroke(const SkConic& conic, SkQuadConstruct* quadPts) {
|
| + ResultType resultType = this->compareQuadConic(conic, quadPts);
|
| + if (kQuad_ResultType == resultType) {
|
| + const SkPoint* stroke = quadPts->fQuad;
|
| + SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
|
| + path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY);
|
| + return true;
|
| + }
|
| + if (kDegenerate_ResultType == resultType) {
|
| + addDegenerateLine(quadPts);
|
| + return true;
|
| + }
|
| + SkDEBUGCODE(gMaxRecursion[kConic_RecursiveLimit] = SkTMax(gMaxRecursion[kConic_RecursiveLimit],
|
| + fRecursionDepth + 1));
|
| + if (++fRecursionDepth > kRecursiveLimits[kConic_RecursiveLimit]) {
|
| + return false; // just abort if projected quad isn't representable
|
| + }
|
| + SkQuadConstruct half;
|
| + (void) half.initWithStart(quadPts);
|
| + if (!this->conicStroke(conic, &half)) {
|
| + return false;
|
| + }
|
| + (void) half.initWithEnd(quadPts);
|
| + if (!this->conicStroke(conic, &half)) {
|
| + return false;
|
| + }
|
| + --fRecursionDepth;
|
| + return true;
|
| +}
|
| +
|
| bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts) {
|
| ResultType resultType = this->compareQuadQuad(quad, quadPts);
|
| if (kQuad_ResultType == resultType) {
|
| @@ -1223,7 +1375,7 @@ bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts)
|
|
|
| void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
|
| const SkPoint& pt3) {
|
| -#if QUAD_STROKE_APPROXIMATION
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| const SkPoint cubic[4] = { fPrevPt, pt1, pt2, pt3 };
|
| SkPoint reduction[3];
|
| const SkPoint* tangentPt;
|
| @@ -1349,13 +1501,6 @@ SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
|
| fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
|
| }
|
|
|
| -#if QUAD_STROKE_APPROXIMATION
|
| -void SkStroke::setError(SkScalar error) {
|
| - SkASSERT(error > 0);
|
| - fError = error;
|
| -}
|
| -#endif
|
| -
|
| void SkStroke::setWidth(SkScalar width) {
|
| SkASSERT(width >= 0);
|
| fWidth = width;
|
| @@ -1432,14 +1577,10 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
|
| }
|
|
|
| SkAutoConicToQuads converter;
|
| +#ifndef SK_QUAD_STROKE_APPROXIMATION
|
| const SkScalar conicTol = SK_Scalar1 / 4 / fResScale;
|
| -
|
| -#if QUAD_STROKE_APPROXIMATION
|
| - SkPathStroker stroker(src, radius, fMiterLimit, fError, this->getCap(),
|
| - this->getJoin(), fResScale);
|
| -#else
|
| - SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(), fResScale);
|
| #endif
|
| + SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(), fResScale);
|
| SkPath::Iter iter(src, false);
|
| SkPath::Verb lastSegment = SkPath::kMove_Verb;
|
|
|
| @@ -1458,6 +1599,11 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
|
| lastSegment = SkPath::kQuad_Verb;
|
| break;
|
| case SkPath::kConic_Verb: {
|
| +#ifdef SK_QUAD_STROKE_APPROXIMATION
|
| + stroker.conicTo(pts[1], pts[2], iter.conicWeight());
|
| + lastSegment = SkPath::kConic_Verb;
|
| + break;
|
| +#else
|
| // todo: if we had maxcurvature for conics, perhaps we should
|
| // natively extrude the conic instead of converting to quads.
|
| const SkPoint* quadPts =
|
| @@ -1467,6 +1613,7 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
|
| quadPts += 2;
|
| }
|
| lastSegment = SkPath::kQuad_Verb;
|
| +#endif
|
| } break;
|
| case SkPath::kCubic_Verb:
|
| stroker.cubicTo(pts[1], pts[2], pts[3]);
|
|
|