diff options
Diffstat (limited to 'src/gpu/ccpr/GrCCPRGeometry.cpp')
-rw-r--r-- | src/gpu/ccpr/GrCCPRGeometry.cpp | 157 |
1 files changed, 144 insertions, 13 deletions
diff --git a/src/gpu/ccpr/GrCCPRGeometry.cpp b/src/gpu/ccpr/GrCCPRGeometry.cpp index f756f6e785..a2c08908bf 100644 --- a/src/gpu/ccpr/GrCCPRGeometry.cpp +++ b/src/gpu/ccpr/GrCCPRGeometry.cpp @@ -8,8 +8,9 @@ #include "GrCCPRGeometry.h" #include "GrTypes.h" +#include "SkGeometry.h" #include "SkPoint.h" -#include "SkNx.h" +#include "../pathops/SkPathOpsCubic.h" #include <algorithm> #include <cmath> #include <cstdlib> @@ -19,6 +20,33 @@ GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT); GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint)); GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX)); +void GrCCPRGeometry::beginPath() { + SkASSERT(!fBuildingContour); + fVerbs.push_back(Verb::kBeginPath); +} + +void GrCCPRGeometry::beginContour(const SkPoint& devPt) { + 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); + fVerbs.push_back(Verb::kBeginContour); + + SkDEBUGCODE(fBuildingContour = true;) +} + +void GrCCPRGeometry::lineTo(const SkPoint& devPt) { + SkASSERT(fBuildingContour); + fCurrFanPoint = devPt; + fPoints.push_back(devPt); + fVerbs.push_back(Verb::kLineTo); +} + static inline Sk2f normalize(const Sk2f& n) { Sk2f nn = n*n; return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt(); @@ -47,17 +75,20 @@ static inline Sk2f lerp(const Sk2f& a, const Sk2f& b, const Sk2f& t) { return SkNx_fma(t, b - a, a); } -bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& controlPt, - const SkPoint& endPt, SkPoint dst[5]) { - Sk2f p0 = Sk2f::Load(&startPt); - Sk2f p1 = Sk2f::Load(&controlPt); - Sk2f p2 = Sk2f::Load(&endPt); +void GrCCPRGeometry::quadraticTo(const SkPoint& devP0, const SkPoint& devP1) { + SkASSERT(fBuildingContour); + + Sk2f p0 = Sk2f::Load(&fCurrFanPoint); + Sk2f p1 = Sk2f::Load(&devP0); + Sk2f p2 = Sk2f::Load(&devP1); + fCurrFanPoint = devP1; Sk2f tan0 = p1 - p0; Sk2f tan1 = p2 - p1; // This should almost always be this case for well-behaved curves in the real world. if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) { - return false; + this->appendMonotonicQuadratic(p1, p2); + return; } // Chop the curve into two segments with equal curvature. To do this we find the T value whose @@ -84,11 +115,111 @@ bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& contro Sk2f p12 = SkNx_fma(t, tan1, p1); Sk2f p012 = lerp(p01, p12, t); - p0.store(&dst[0]); - p01.store(&dst[1]); - p012.store(&dst[2]); - p12.store(&dst[3]); - p2.store(&dst[4]); + this->appendMonotonicQuadratic(p01, p012); + this->appendMonotonicQuadratic(p12, p2); +} + +inline void GrCCPRGeometry::appendMonotonicQuadratic(const Sk2f& p1, const Sk2f& p2) { + p1.store(&fPoints.push_back()); + p2.store(&fPoints.push_back()); + fVerbs.push_back(Verb::kMonotonicQuadraticTo); + ++fCurrContourTallies.fQuadratics; +} + +void GrCCPRGeometry::cubicTo(const SkPoint& devP1, const SkPoint& devP2, const SkPoint& devP3) { + SkASSERT(fBuildingContour); + + SkPoint P[4] = {fCurrFanPoint, devP1, devP2, devP3}; + double t[2], s[2]; + SkCubicType type = SkClassifyCubic(P, t, s); + + if (SkCubicType::kLineOrPoint == type) { + this->lineTo(P[3]); + return; + } + + if (SkCubicType::kQuadratic == type) { + SkPoint quadP1 = (devP1 + devP2) * .75f - (fCurrFanPoint + devP3) * .25f; + this->quadraticTo(quadP1, devP3); + return; + } + + fCurrFanPoint = devP3; + + SkDCubic C; + C.set(P); + + for (int x = 0; x <= 1; ++x) { + if (t[x] * s[x] <= 0) { // This is equivalent to tx/sx <= 0. + // This technically also gets taken if tx/sx = infinity, but the code still does + // the right thing in that edge case. + continue; // Don't increment x0. + } + if (fabs(t[x]) >= fabs(s[x])) { // tx/sx >= 1. + break; + } + + const double chopT = double(t[x]) / double(s[x]); + SkASSERT(chopT >= 0 && chopT <= 1); + if (chopT <= 0 || chopT >= 1) { // floating-point error. + continue; + } + + SkDCubicPair chopped = C.chopAt(chopT); + + // Ensure the double points are identical if this is a loop (more workarounds for FP error). + if (SkCubicType::kLoop == type && 0 == t[0]) { + chopped.pts[3] = chopped.pts[0]; + } + + // (This might put ts0/ts1 out of order, but it doesn't matter anymore at this point.) + this->appendConvexCubic(type, chopped.first()); + t[x] = 0; + s[x] = 1; + + const double r = s[1 - x] * chopT; + t[1 - x] -= r; + s[1 - x] -= r; + + C = chopped.second(); + } + + this->appendConvexCubic(type, C); +} + +static SkPoint to_skpoint(const SkDPoint& dpoint) { + return {static_cast<SkScalar>(dpoint.fX), static_cast<SkScalar>(dpoint.fY)}; +} + +inline void GrCCPRGeometry::appendConvexCubic(SkCubicType type, const SkDCubic& C) { + fPoints.push_back(to_skpoint(C[1])); + fPoints.push_back(to_skpoint(C[2])); + fPoints.push_back(to_skpoint(C[3])); + if (SkCubicType::kLoop != type) { + fVerbs.push_back(Verb::kConvexSerpentineTo); + ++fCurrContourTallies.fSerpentines; + } else { + fVerbs.push_back(Verb::kConvexLoopTo); + ++fCurrContourTallies.fLoops; + } +} + +GrCCPRGeometry::PrimitiveTallies GrCCPRGeometry::endContour() { + SkASSERT(fBuildingContour); + SkASSERT(fVerbs.count() >= fCurrContourTallies.fTriangles); + + // 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) { + --fanSize; + fVerbs.push_back(Verb::kEndClosedContour); + } else { + fVerbs.push_back(Verb::kEndOpenContour); + } + + fCurrContourTallies.fTriangles = SkTMax(fanSize - 2, 0); - return true; + SkDEBUGCODE(fBuildingContour = false;) + return fCurrContourTallies; } |