diff options
-rw-r--r-- | samplecode/SampleCCPRGeometry.cpp | 7 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCGeometry.cpp | 115 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCGeometry.h | 12 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCPathParser.cpp | 5 |
4 files changed, 69 insertions, 70 deletions
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp index 7ddf91f0ae..a6e408749a 100644 --- a/samplecode/SampleCCPRGeometry.cpp +++ b/samplecode/SampleCCPRGeometry.cpp @@ -245,7 +245,7 @@ void CCPRGeometryView::updateGpuData() { fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s); GrCCGeometry geometry; geometry.beginContour(fPoints[0]); - geometry.cubicTo(fPoints[1], fPoints[2], fPoints[3], kDebugBloat / 2, kDebugBloat / 2); + geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2); geometry.endContour(); int ptsIdx = 0; for (GrCCGeometry::Verb verb : geometry.verbs()) { @@ -265,9 +265,10 @@ void CCPRGeometryView::updateGpuData() { } } } else if (PrimitiveType::kQuadratics == fPrimitiveType) { + SkPoint P3[3] = {fPoints[0], fPoints[1], fPoints[3]}; GrCCGeometry geometry; - geometry.beginContour(fPoints[0]); - geometry.quadraticTo(fPoints[1], fPoints[3]); + geometry.beginContour(P3[0]); + geometry.quadraticTo(P3); geometry.endContour(); int ptsIdx = 0; for (GrCCGeometry::Verb verb : geometry.verbs()) { diff --git a/src/gpu/ccpr/GrCCGeometry.cpp b/src/gpu/ccpr/GrCCGeometry.cpp index 481f4e4725..c289c40453 100644 --- a/src/gpu/ccpr/GrCCGeometry.cpp +++ b/src/gpu/ccpr/GrCCGeometry.cpp @@ -23,26 +23,27 @@ void GrCCGeometry::beginPath() { fVerbs.push_back(Verb::kBeginPath); } -void GrCCGeometry::beginContour(const SkPoint& devPt) { +void GrCCGeometry::beginContour(const SkPoint& pt) { SkASSERT(!fBuildingContour); - - fCurrFanPoint = fCurrAnchorPoint = devPt; - // Store the current verb count in the fTriangles field for now. When we close the contour we // will use this value to calculate the actual number of triangles in its fan. fCurrContourTallies = {fVerbs.count(), 0, 0, 0}; - fPoints.push_back(devPt); + fPoints.push_back(pt); fVerbs.push_back(Verb::kBeginContour); + fCurrAnchorPoint = pt; SkDEBUGCODE(fBuildingContour = true); } -void GrCCGeometry::lineTo(const SkPoint& devPt) { +void GrCCGeometry::lineTo(const SkPoint& pt) { SkASSERT(fBuildingContour); - SkASSERT(fCurrFanPoint == fPoints.back()); - fCurrFanPoint = devPt; - fPoints.push_back(devPt); + fPoints.push_back(pt); + fVerbs.push_back(Verb::kLineTo); +} + +void GrCCGeometry::appendLine(const Sk2f& endpt) { + endpt.store(&fPoints.push_back()); fVerbs.push_back(Verb::kLineTo); } @@ -74,11 +75,11 @@ static inline bool are_collinear(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) } // Returns whether the (convex) curve segment is monotonic with respect to [endPt - startPt]. -static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& startTan, - const Sk2f& endPt, const Sk2f& endTan) { +static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& tan0, + const Sk2f& endPt, const Sk2f& tan1) { Sk2f v = endPt - startPt; - float dot0 = dot(startTan, v); - float dot1 = dot(endTan, v); + float dot0 = dot(tan0, v); + float dot1 = dot(tan1, v); // A small, negative tolerance handles floating-point error in the case when one tangent // approaches 0 length, meaning the (convex) curve segment is effectively a flat line. @@ -90,14 +91,19 @@ static inline Sk2f lerp(const Sk2f& a, const Sk2f& b, const Sk2f& t) { return SkNx_fma(t, b - a, a); } -void GrCCGeometry::quadraticTo(const SkPoint& devP0, const SkPoint& devP1) { +void GrCCGeometry::quadraticTo(const SkPoint P[3]) { SkASSERT(fBuildingContour); - SkASSERT(fCurrFanPoint == fPoints.back()); + SkASSERT(P[0] == fPoints.back()); + Sk2f p0 = Sk2f::Load(P); + Sk2f p1 = Sk2f::Load(P+1); + Sk2f p2 = Sk2f::Load(P+2); - Sk2f p0 = Sk2f::Load(&fCurrFanPoint); - Sk2f p1 = Sk2f::Load(&devP0); - Sk2f p2 = Sk2f::Load(&devP1); - fCurrFanPoint = devP1; + // Don't crunch on the curve if it is nearly flat (or just very small). Flat curves can break + // The monotonic chopping math. + if (are_collinear(p0, p1, p2)) { + this->appendLine(p2); + return; + } this->appendMonotonicQuadratics(p0, p1, p2); } @@ -114,10 +120,10 @@ inline void GrCCGeometry::appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& } // Chop the curve into two segments with equal curvature. To do this we find the T value whose - // tangent is perpendicular to the vector that bisects tan0 and -tan1. + // tangent angle is halfway between tan0 and tan1. Sk2f n = normalize(tan0) - normalize(tan1); - // This tangent can be found where (dQ(t) dot n) = 0: + // The midtangent can be found where (dQ(t) dot n) = 0: // // 0 = (dQ(t) dot n) = | 2*t 1 | * | p0 - 2*p1 + p2 | * | n | // | -2*p0 + 2*p1 | | . | @@ -147,8 +153,7 @@ inline void GrCCGeometry::appendSingleMonotonicQuadratic(const Sk2f& p0, const S // Don't send curves to the GPU if we know they are nearly flat (or just very small). if (are_collinear(p0, p1, p2)) { - p2.store(&fPoints.push_back()); - fVerbs.push_back(Verb::kLineTo); + this->appendLine(p2); return; } @@ -274,51 +279,47 @@ static inline Sk2f first_unless_nearly_zero(const Sk2f& a, const Sk2f& b) { } static inline bool is_cubic_nearly_quadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, - const Sk2f& p3, Sk2f& tan0, Sk2f& tan3, Sk2f& c) { + const Sk2f& p3, Sk2f& tan0, Sk2f& tan1, Sk2f& c) { tan0 = first_unless_nearly_zero(p1 - p0, p2 - p0); - tan3 = first_unless_nearly_zero(p3 - p2, p3 - p1); + tan1 = first_unless_nearly_zero(p3 - p2, p3 - p1); Sk2f c1 = SkNx_fma(Sk2f(1.5f), tan0, p0); - Sk2f c2 = SkNx_fma(Sk2f(-1.5f), tan3, p3); + Sk2f c2 = SkNx_fma(Sk2f(-1.5f), tan1, p3); c = (c1 + c2) * .5f; // Hopefully optimized out if not used? return ((c1 - c2).abs() <= 1).allTrue(); } -void GrCCGeometry::cubicTo(const SkPoint& devP1, const SkPoint& devP2, const SkPoint& devP3, - float inflectPad, float loopIntersectPad) { +void GrCCGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopIntersectPad) { SkASSERT(fBuildingContour); - SkASSERT(fCurrFanPoint == fPoints.back()); - - SkPoint devPts[4] = {fCurrFanPoint, devP1, devP2, devP3}; - Sk2f p0 = Sk2f::Load(&fCurrFanPoint); - Sk2f p1 = Sk2f::Load(&devP1); - Sk2f p2 = Sk2f::Load(&devP2); - Sk2f p3 = Sk2f::Load(&devP3); - fCurrFanPoint = devP3; - - // Don't crunch on the curve and inflate geometry if it is nearly flat (or just very small). + SkASSERT(P[0] == fPoints.back()); + Sk2f p0 = Sk2f::Load(P); + Sk2f p1 = Sk2f::Load(P+1); + Sk2f p2 = Sk2f::Load(P+2); + Sk2f p3 = Sk2f::Load(P+3); + + // Don't crunch on the curve or inflate geometry if it is nearly flat (or just very small). + // Flat curves can break the math below. if (are_collinear(p0, p1, p2) && are_collinear(p1, p2, p3) && are_collinear(p0, (p1 + p2) * .5f, p3)) { - p3.store(&fPoints.push_back()); - fVerbs.push_back(Verb::kLineTo); + this->appendLine(p3); return; } // Also detect near-quadratics ahead of time. - Sk2f tan0, tan3, c; - if (is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan3, c)) { + Sk2f tan0, tan1, c; + if (is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan1, c)) { this->appendMonotonicQuadratics(p0, c, p3); return; } double tt[2], ss[2]; - fCurrCubicType = SkClassifyCubic(devPts, tt, ss); + fCurrCubicType = SkClassifyCubic(P, tt, ss); SkASSERT(!SkCubicIsDegenerate(fCurrCubicType)); // Should have been caught above. SkMatrix CIT; - ExcludedTerm skipTerm = GrPathUtils::calcCubicInverseTransposePowerBasisMatrix(devPts, &CIT); + ExcludedTerm skipTerm = GrPathUtils::calcCubicInverseTransposePowerBasisMatrix(P, &CIT); SkASSERT(ExcludedTerm::kNonInvertible != skipTerm); // Should have been caught above. SkASSERT(0 == CIT[6]); SkASSERT(0 == CIT[7]); @@ -460,9 +461,9 @@ void GrCCGeometry::cubicTo(const SkPoint& devP1, const SkPoint& devP2, const SkP template<GrCCGeometry::AppendCubicFn AppendLeftRight> inline void GrCCGeometry::chopCubicAtMidTangent(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, const Sk2f& p3, const Sk2f& tan0, - const Sk2f& tan3, int maxFutureSubdivisions) { - // Find the T value whose tangent is perpendicular to the vector that bisects tan0 and -tan3. - Sk2f n = normalize(tan0) - normalize(tan3); + const Sk2f& tan1, int maxFutureSubdivisions) { + // Find the T value whose tangent is perpendicular to the vector that bisects tan0 and -tan1. + Sk2f n = normalize(tan0) - normalize(tan1); float a = 3 * dot(p3 + (p1 - p2)*3 - p0, n); float b = 6 * dot(p0 - p1*2 + p2, n); @@ -515,11 +516,11 @@ void GrCCGeometry::appendMonotonicCubics(const Sk2f& p0, const Sk2f& p1, const S if (maxSubdivisions) { Sk2f tan0 = first_unless_nearly_zero(p1 - p0, p2 - p0); - Sk2f tan3 = first_unless_nearly_zero(p3 - p2, p3 - p1); + Sk2f tan1 = first_unless_nearly_zero(p3 - p2, p3 - p1); - if (!is_convex_curve_monotonic(p0, tan0, p3, tan3)) { + if (!is_convex_curve_monotonic(p0, tan0, p3, tan1)) { this->chopCubicAtMidTangent<&GrCCGeometry::appendMonotonicCubics>(p0, p1, p2, p3, - tan0, tan3, + tan0, tan1, maxSubdivisions - 1); return; } @@ -530,8 +531,7 @@ void GrCCGeometry::appendMonotonicCubics(const Sk2f& p0, const Sk2f& p1, const S // Don't send curves to the GPU if we know they are nearly flat (or just very small). // Since the cubic segment is known to be convex at this point, our flatness check is simple. if (are_collinear(p0, (p1 + p2) * .5f, p3)) { - p3.store(&fPoints.push_back()); - fVerbs.push_back(Verb::kLineTo); + this->appendLine(p3); return; } @@ -554,15 +554,14 @@ void GrCCGeometry::appendCubicApproximation(const Sk2f& p0, const Sk2f& p1, cons // This can cause some curves to feel slightly more flat when inspected rigorously back and // forth against another renderer, but for now this seems acceptable given the simplicity. SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); - p3.store(&fPoints.push_back()); - fVerbs.push_back(Verb::kLineTo); + this->appendLine(p3); return; } - Sk2f tan0, tan3, c; - if (!is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan3, c) && maxSubdivisions) { + Sk2f tan0, tan1, c; + if (!is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan1, c) && maxSubdivisions) { this->chopCubicAtMidTangent<&GrCCGeometry::appendCubicApproximation>(p0, p1, p2, p3, - tan0, tan3, + tan0, tan1, maxSubdivisions - 1); return; } @@ -581,7 +580,7 @@ GrCCGeometry::PrimitiveTallies GrCCGeometry::endContour() { // The fTriangles field currently contains this contour's starting verb index. We can now // use it to calculate the size of the contour's fan. int fanSize = fVerbs.count() - fCurrContourTallies.fTriangles; - if (fCurrFanPoint == fCurrAnchorPoint) { + if (fPoints.back() == fCurrAnchorPoint) { --fanSize; fVerbs.push_back(Verb::kEndClosedContour); } else { diff --git a/src/gpu/ccpr/GrCCGeometry.h b/src/gpu/ccpr/GrCCGeometry.h index f593744b70..01cf16c68d 100644 --- a/src/gpu/ccpr/GrCCGeometry.h +++ b/src/gpu/ccpr/GrCCGeometry.h @@ -72,9 +72,9 @@ public: } void beginPath(); - void beginContour(const SkPoint& devPt); - void lineTo(const SkPoint& devPt); - void quadraticTo(const SkPoint& devP1, const SkPoint& devP2); + void beginContour(const SkPoint&); + void lineTo(const SkPoint&); + void quadraticTo(const SkPoint[3]); // We pass through inflection points and loop intersections using a line and quadratic(s) // respectively. 'inflectPad' and 'loopIntersectPad' specify how close (in pixels) cubic @@ -87,12 +87,13 @@ public: // through the loop intersection can be approximated with a single quadratic anyway, // regardless of whether we are use one pixel of pad or two (1.622 avg. quads per loop // intersection vs. 1.489 on the tiger). - void cubicTo(const SkPoint& devP1, const SkPoint& devP2, const SkPoint& devP3, - float inflectPad = 0.55f, float loopIntersectPad = 2); + void cubicTo(const SkPoint[4], float inflectPad = 0.55f, float loopIntersectPad = 2); PrimitiveTallies endContour(); // Returns the numbers of primitives needed to draw the contour. private: + inline void appendLine(const Sk2f& endpt); + inline void appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2); inline void appendSingleMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2); @@ -117,7 +118,6 @@ private: // Transient state used while building a contour. SkPoint fCurrAnchorPoint; - SkPoint fCurrFanPoint; PrimitiveTallies fCurrContourTallies; SkCubicType fCurrCubicType; SkDEBUGCODE(bool fBuildingContour = false); diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp index 506efc752f..f77c52e6f1 100644 --- a/src/gpu/ccpr/GrCCPathParser.cpp +++ b/src/gpu/ccpr/GrCCPathParser.cpp @@ -134,12 +134,11 @@ void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts ++ptsIdx; continue; case SkPath::kQuad_Verb: - fGeometry.quadraticTo(deviceSpacePts[ptsIdx], deviceSpacePts[ptsIdx + 1]); + fGeometry.quadraticTo(&deviceSpacePts[ptsIdx - 1]); ptsIdx += 2; continue; case SkPath::kCubic_Verb: - fGeometry.cubicTo(deviceSpacePts[ptsIdx], deviceSpacePts[ptsIdx + 1], - deviceSpacePts[ptsIdx + 2]); + fGeometry.cubicTo(&deviceSpacePts[ptsIdx - 1]); ptsIdx += 3; continue; case SkPath::kConic_Verb: |