aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/strokes.cpp30
-rw-r--r--samplecode/SampleQuadStroker.cpp4
-rw-r--r--src/core/SkGeometry.cpp2
-rw-r--r--src/core/SkGeometry.h2
-rw-r--r--src/core/SkStroke.cpp113
-rw-r--r--tools/pathops_sorter.htm7
6 files changed, 104 insertions, 54 deletions
diff --git a/gm/strokes.cpp b/gm/strokes.cpp
index a258dfe005..9de20911ce 100644
--- a/gm/strokes.cpp
+++ b/gm/strokes.cpp
@@ -241,12 +241,42 @@ private:
typedef skiagm::GM INHERITED;
};
+class Strokes4GM : public skiagm::GM {
+public:
+ Strokes4GM() {}
+
+protected:
+
+ SkString onShortName() override {
+ return SkString("strokes_zoomed");
+ }
+
+ SkISize onISize() override {
+ return SkISize::Make(W, H*2);
+ }
+
+ void onDraw(SkCanvas* canvas) override {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(0.055f);
+
+ canvas->scale(1000, 1000);
+ canvas->drawCircle(0, 2, 1.97f, paint);
+ }
+
+private:
+ typedef skiagm::GM INHERITED;
+};
+
+
//////////////////////////////////////////////////////////////////////////////
static skiagm::GM* F0(void*) { return new StrokesGM; }
static skiagm::GM* F1(void*) { return new Strokes2GM; }
static skiagm::GM* F2(void*) { return new Strokes3GM; }
+static skiagm::GM* F3(void*) { return new Strokes4GM; }
static skiagm::GMRegistry R0(F0);
static skiagm::GMRegistry R1(F1);
static skiagm::GMRegistry R2(F2);
+static skiagm::GMRegistry R3(F3);
diff --git a/samplecode/SampleQuadStroker.cpp b/samplecode/SampleQuadStroker.cpp
index 73373a2c97..05b6204026 100644
--- a/samplecode/SampleQuadStroker.cpp
+++ b/samplecode/SampleQuadStroker.cpp
@@ -304,10 +304,10 @@ protected:
void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, SkScalar scale,
bool drawText) {
- SkRect bounds = path.getBounds();
- if (bounds.isEmpty()) {
+ if (path.isEmpty()) {
return;
}
+ SkRect bounds = path.getBounds();
this->setWHZ(SkScalarCeilToInt(bounds.right()), drawText
? SkScalarRoundToInt(scale * 3 / 2) : SkScalarRoundToInt(scale),
SkScalarRoundToInt(950.0f / scale));
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
index b4489d28e3..2cda344a5d 100644
--- a/src/core/SkGeometry.cpp
+++ b/src/core/SkGeometry.cpp
@@ -1474,10 +1474,12 @@ void SkConic::computeFastBounds(SkRect* bounds) const {
bounds->set(fPts, 3);
}
+#if 0 // unimplemented
bool SkConic::findMaxCurvature(SkScalar* t) const {
// TODO: Implement me
return false;
}
+#endif
SkScalar SkConic::TransformW(const SkPoint pts[], SkScalar w,
const SkMatrix& matrix) {
diff --git a/src/core/SkGeometry.h b/src/core/SkGeometry.h
index a0c13d6046..bd3abe1acf 100644
--- a/src/core/SkGeometry.h
+++ b/src/core/SkGeometry.h
@@ -276,7 +276,7 @@ struct SkConic {
*
* @return true if max curvature found inside 0..1 range, false otherwise
*/
- bool findMaxCurvature(SkScalar* t) const;
+// bool findMaxCurvature(SkScalar* t) const; // unimplemented
static SkScalar TransformW(const SkPoint[3], SkScalar w, const SkMatrix&);
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
index 6454f1694d..b4af918593 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);
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index 3411505eb7..4fb18fad64 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -7,9 +7,10 @@
<div style="height:0">
<div id="sect1">
-{{{6, -3}, {0, 1}}} id=3
-{{{1.6714313f, -1.08141601f}, {2.24979973f, -1.14467525f}, {2.27122664f, -0.514151096f}, {0, 1}}} id=5
-{{{0.001119050197303295135, 0.9992539882659912109}, {4.001119136810302734, 6.999254226684570312}}},
+{{{fX=1.97000003 fY=2.00000000 }, {fX=1.97000003 fY=3.97000003 }, {fX=0.000000000 fY=3.97000003 }} fW: 0.707106769
+{{{fX=1.99750006 fY=2.00000000 } {fX=1.99750006 fY=2.02749991 }
+{{{fX=1.96415257 fY=2.36347127 } {fX=1.95914865 fY=2.39051223 }
+
</div>
</div>