void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, int oppWinding) { const SkPoint& pt = xyAtT(&span); SkDebugf("%s id=%d", fun, fID); SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); } SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther-> fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]); SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=", span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, (&span)[1].fT, winding, oppWinding); if (span.fOppSum == SK_MinS32) { SkDebugf("?"); } else { SkDebugf("%d", span.fOppSum); } SkDebugf(" windSum="); if (span.fWindSum == SK_MinS32) { SkDebugf("?"); } else { SkDebugf("%d", span.fWindSum); } SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue); }
void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const { const SkOpSegment& segment = fSegments[index]; if (0 == zeroPt) { *point = segment.pts()[0]; } else { *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())]; } }
static bool can_add_curve(SkPath::Verb verb, SkPoint* curve) { if (SkPath::kMove_Verb == verb) { return false; } for (int index = 0; index <= SkPathOpsVerbToPoints(verb); ++index) { force_small_to_zero(&curve[index]); } return SkPath::kLine_Verb != verb || !SkDPoint::ApproximatelyEqual(curve[0], curve[1]); }
void SkOpSegment::dumpPts() const { int last = SkPathOpsVerbToPoints(fVerb); SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID()); int index = 0; do { SkDPoint::Dump(fPts[index]); SkDebugf(", "); } while (++index < last); SkDPoint::Dump(fPts[index]); SkDebugf("}}\n"); }
bool SkOpEdgeBuilder::walk() { uint8_t* verbPtr = fPathVerbs.begin(); uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; const SkPoint* pointsPtr = fPathPts.begin() - 1; SkPath::Verb verb; while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) { if (verbPtr == endOfFirstHalf) { fOperand = true; } verbPtr++; switch (verb) { case SkPath::kMove_Verb: if (fCurrentContour) { if (fAllowOpenContours) { complete(); } else if (!close()) { return false; } } if (!fCurrentContour) { fCurrentContour = fContours.push_back_n(1); fCurrentContour->setOperand(fOperand); fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask); } pointsPtr += 1; continue; case SkPath::kLine_Verb: fCurrentContour->addLine(pointsPtr); break; case SkPath::kQuad_Verb: fCurrentContour->addQuad(pointsPtr); break; case SkPath::kCubic_Verb: fCurrentContour->addCubic(pointsPtr); break; case SkPath::kClose_Verb: SkASSERT(fCurrentContour); if (!close()) { return false; } continue; default: SkDEBUGFAIL("bad verb"); return false; } pointsPtr += SkPathOpsVerbToPoints(verb); SkASSERT(fCurrentContour); } if (fCurrentContour && !fAllowOpenContours && !close()) { return false; } return true; }
void SkOpSegment::dumpDPts() const { int count = SkPathOpsVerbToPoints(fVerb); SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID()); int index = 0; do { SkDPoint dPt = {fPts[index].fX, fPts[index].fY}; dPt.dump(); if (index != count) { SkDebugf(", "); } } while (++index <= count); SkDebugf("}}\n"); }
// OPTIMIZATION: longest can all be either lazily computed here or precomputed in setup double SkOpAngle::distEndRatio(double dist) const { double longest = 0; const SkOpSegment& segment = *this->segment(); int ptCount = SkPathOpsVerbToPoints(segment.verb()); const SkPoint* pts = segment.pts(); for (int idx1 = 0; idx1 <= ptCount - 1; ++idx1) { for (int idx2 = idx1 + 1; idx2 <= ptCount; ++idx2) { if (idx1 == idx2) { continue; } SkDVector v; v.set(pts[idx2] - pts[idx1]); double lenSq = v.lengthSquared(); longest = SkTMax(longest, lenSq); } } return sqrt(longest) / dist; }
// given a line, see if the opposite curve's convex hull is all on one side // returns -1=not on one side 0=this CW of test 1=this CCW of test int SkOpAngle::allOnOneSide(const SkOpAngle& test) const { SkASSERT(!fIsCurve); SkASSERT(test.fIsCurve); const SkDPoint& origin = test.fCurvePart[0]; SkVector line; if (fSegment->verb() == SkPath::kLine_Verb) { const SkPoint* linePts = fSegment->pts(); int lineStart = fStart < fEnd ? 0 : 1; line = linePts[lineStart ^ 1] - linePts[lineStart]; } else { SkPoint shortPts[2] = { fCurvePart[0].asSkPoint(), fCurvePart[1].asSkPoint() }; line = shortPts[1] - shortPts[0]; } float crosses[3]; SkPath::Verb testVerb = test.fSegment->verb(); int iMax = SkPathOpsVerbToPoints(testVerb); // SkASSERT(origin == test.fCurveHalf[0]); const SkDCubic& testCurve = test.fCurvePart; // do { for (int index = 1; index <= iMax; ++index) { float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY)); float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX)); crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2; } if (crosses[0] * crosses[1] < 0) { return -1; } if (SkPath::kCubic_Verb == testVerb) { if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) { return -1; } } if (crosses[0]) { return crosses[0] < 0; } if (crosses[1]) { return crosses[1] < 0; } if (SkPath::kCubic_Verb == testVerb && crosses[2]) { return crosses[2] < 0; } fUnorderable = true; return -1; }
void SkOpSegment::debugShowActiveSpans() const { debugValidate(); if (done()) { return; } #if DEBUG_ACTIVE_SPANS_SHORT_FORM int lastId = -1; double lastT = -1; #endif for (int i = 0; i < fTs.count(); ++i) { if (fTs[i].fDone) { continue; } SK_ALWAYSBREAK(i < fTs.count() - 1); #if DEBUG_ACTIVE_SPANS_SHORT_FORM if (lastId == fID && lastT == fTs[i].fT) { continue; } lastId = fID; lastT = fTs[i].fT; #endif SkDebugf("%s id=%d", __FUNCTION__, fID); SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); } const SkOpSpan* span = &fTs[i]; SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span)); int iEnd = i + 1; while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) { ++iEnd; } SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT); const SkOpSegment* other = fTs[i].fOther; SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=", other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex); if (fTs[i].fWindSum == SK_MinS32) { SkDebugf("?"); } else { SkDebugf("%d", fTs[i].fWindSum); } SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue); } }
int SkOpEdgeBuilder::preFetch() { if (!fPath->isFinite()) { fUnparseable = true; return 0; } SkAutoConicToQuads quadder; const SkScalar quadderTol = SK_Scalar1 / 16; SkPath::RawIter iter(*fPath); SkPoint curveStart; SkPoint curve[4]; SkPoint pts[4]; SkPath::Verb verb; bool lastCurve = false; do { verb = iter.next(pts); switch (verb) { case SkPath::kMove_Verb: if (!fAllowOpenContours && lastCurve) { closeContour(curve[0], curveStart); } fPathVerbs.push_back(verb); force_small_to_zero(&pts[0]); fPathPts.push_back(pts[0]); curveStart = curve[0] = pts[0]; lastCurve = false; continue; case SkPath::kLine_Verb: force_small_to_zero(&pts[1]); if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) { uint8_t lastVerb = fPathVerbs.back(); if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) { fPathPts.back() = pts[1]; } continue; // skip degenerate points } break; case SkPath::kQuad_Verb: force_small_to_zero(&pts[1]); force_small_to_zero(&pts[2]); curve[1] = pts[1]; curve[2] = pts[2]; verb = SkReduceOrder::Quad(curve, pts); if (verb == SkPath::kMove_Verb) { continue; // skip degenerate points } break; case SkPath::kConic_Verb: { const SkPoint* quadPts = quadder.computeQuads(pts, iter.conicWeight(), quadderTol); const int nQuads = quadder.countQuads(); for (int i = 0; i < nQuads; ++i) { fPathVerbs.push_back(SkPath::kQuad_Verb); } fPathPts.push_back_n(nQuads * 2, &quadPts[1]); curve[0] = pts[2]; lastCurve = true; } continue; case SkPath::kCubic_Verb: force_small_to_zero(&pts[1]); force_small_to_zero(&pts[2]); force_small_to_zero(&pts[3]); curve[1] = pts[1]; curve[2] = pts[2]; curve[3] = pts[3]; verb = SkReduceOrder::Cubic(curve, pts); if (verb == SkPath::kMove_Verb) { continue; // skip degenerate points } break; case SkPath::kClose_Verb: closeContour(curve[0], curveStart); lastCurve = false; continue; case SkPath::kDone_Verb: continue; } fPathVerbs.push_back(verb); int ptCount = SkPathOpsVerbToPoints(verb); fPathPts.push_back_n(ptCount, &pts[1]); curve[0] = pts[ptCount]; lastCurve = true; } while (verb != SkPath::kDone_Verb); if (!fAllowOpenContours && lastCurve) { closeContour(curve[0], curveStart); } fPathVerbs.push_back(SkPath::kDone_Verb); return fPathVerbs.count() - 1; }
int SkOpEdgeBuilder::preFetch() { if (!fPath->isFinite()) { fUnparseable = true; return 0; } SkPath::RawIter iter(*fPath); SkPoint curveStart; SkPoint curve[4]; SkPoint pts[4]; SkPath::Verb verb; bool lastCurve = false; do { verb = iter.next(pts); switch (verb) { case SkPath::kMove_Verb: if (!fAllowOpenContours && lastCurve) { closeContour(curve[0], curveStart); } *fPathVerbs.append() = verb; force_small_to_zero(&pts[0]); *fPathPts.append() = pts[0]; curveStart = curve[0] = pts[0]; lastCurve = false; continue; case SkPath::kLine_Verb: force_small_to_zero(&pts[1]); if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) { uint8_t lastVerb = fPathVerbs.top(); if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) { fPathPts.top() = pts[1]; } continue; // skip degenerate points } break; case SkPath::kQuad_Verb: force_small_to_zero(&pts[1]); force_small_to_zero(&pts[2]); curve[1] = pts[1]; curve[2] = pts[2]; verb = SkReduceOrder::Quad(curve, pts); if (verb == SkPath::kMove_Verb) { continue; // skip degenerate points } break; case SkPath::kConic_Verb: force_small_to_zero(&pts[1]); force_small_to_zero(&pts[2]); curve[1] = pts[1]; curve[2] = pts[2]; verb = SkReduceOrder::Quad(curve, pts); if (SkPath::kQuad_Verb == verb && 1 != iter.conicWeight()) { verb = SkPath::kConic_Verb; } else if (verb == SkPath::kMove_Verb) { continue; // skip degenerate points } break; case SkPath::kCubic_Verb: force_small_to_zero(&pts[1]); force_small_to_zero(&pts[2]); force_small_to_zero(&pts[3]); curve[1] = pts[1]; curve[2] = pts[2]; curve[3] = pts[3]; verb = SkReduceOrder::Cubic(curve, pts); if (verb == SkPath::kMove_Verb) { continue; // skip degenerate points } break; case SkPath::kClose_Verb: closeContour(curve[0], curveStart); lastCurve = false; continue; case SkPath::kDone_Verb: continue; } *fPathVerbs.append() = verb; int ptCount = SkPathOpsVerbToPoints(verb); fPathPts.append(ptCount, &pts[1]); if (verb == SkPath::kConic_Verb) { *fWeights.append() = iter.conicWeight(); } curve[0] = pts[ptCount]; lastCurve = true; } while (verb != SkPath::kDone_Verb); if (!fAllowOpenContours && lastCurve) { closeContour(curve[0], curveStart); } *fPathVerbs.append() = SkPath::kDone_Verb; return fPathVerbs.count() - 1; }
bool SkOpEdgeBuilder::walk() { uint8_t* verbPtr = fPathVerbs.begin(); uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; SkPoint* pointsPtr = fPathPts.begin() - 1; SkScalar* weightPtr = fWeights.begin(); SkPath::Verb verb; while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) { if (verbPtr == endOfFirstHalf) { fOperand = true; } verbPtr++; switch (verb) { case SkPath::kMove_Verb: if (fCurrentContour && fCurrentContour->count()) { if (fAllowOpenContours) { complete(); } else if (!close()) { return false; } } if (!fCurrentContour) { fCurrentContour = fContoursHead->appendContour(); } fCurrentContour->init(fGlobalState, fOperand, fXorMask[fOperand] == kEvenOdd_PathOpsMask); pointsPtr += 1; continue; case SkPath::kLine_Verb: fCurrentContour->addLine(pointsPtr); break; case SkPath::kQuad_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; if (v1.dot(v2) < 0) { SkPoint pair[5]; if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) { goto addOneQuad; } if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { return false; } SkPoint cStorage[2][2]; SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? &pair[0] : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? &pair[2] : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fCurrentContour->addCurve(v1, curve1); fCurrentContour->addCurve(v2, curve2); break; } } } addOneQuad: fCurrentContour->addQuad(pointsPtr); break; case SkPath::kConic_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; SkScalar weight = *weightPtr++; if (v1.dot(v2) < 0) { // FIXME: max curvature for conics hasn't been implemented; use placeholder SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr); if (maxCurvature > 0) { SkConic conic(pointsPtr, weight); SkConic pair[2]; if (!conic.chopAt(maxCurvature, pair)) { // if result can't be computed, use original fCurrentContour->addConic(pointsPtr, weight); break; } SkPoint cStorage[2][3]; SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? pair[0].fPts : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? pair[1].fPts : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fCurrentContour->addCurve(v1, curve1, pair[0].fW); fCurrentContour->addCurve(v2, curve2, pair[1].fW); break; } } } fCurrentContour->addConic(pointsPtr, weight); } break; case SkPath::kCubic_Verb: { // Split complex cubics (such as self-intersecting curves or // ones with difficult curvature) in two before proceeding. // This can be required for intersection to succeed. SkScalar splitT; if (SkDCubic::ComplexBreak(pointsPtr, &splitT)) { SkPoint pair[7]; SkChopCubicAt(pointsPtr, pair, splitT); if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { return false; } SkPoint cStorage[2][4]; SkPath::Verb v1 = SkReduceOrder::Cubic(&pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Cubic(&pair[3], cStorage[1]); SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &pair[0] : cStorage[0]; SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &pair[3] : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fCurrentContour->addCurve(v1, curve1); fCurrentContour->addCurve(v2, curve2); break; } } } fCurrentContour->addCubic(pointsPtr); break; case SkPath::kClose_Verb: SkASSERT(fCurrentContour); if (!close()) { return false; } continue; default: SkDEBUGFAIL("bad verb"); return false; } SkASSERT(fCurrentContour); fCurrentContour->debugValidate(); pointsPtr += SkPathOpsVerbToPoints(verb); } if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) { return false; } return true; }
bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const { SkPath::Verb lVerb = fSegment->verb(); SkPath::Verb rVerb = rh.fSegment->verb(); int lPts = SkPathOpsVerbToPoints(lVerb); int rPts = SkPathOpsVerbToPoints(rVerb); SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}}, {{fCurvePart[0], fCurvePart[lPts]}}}; if (rays[0][1] == rays[1][1]) { return checkParallel(rh); } double smallTs[2] = {-1, -1}; bool limited[2] = {false, false}; for (int index = 0; index < 2; ++index) { const SkOpSegment& segment = index ? *rh.fSegment : *fSegment; SkIntersections i; (*CurveIntersectRay[index ? rPts : lPts])(segment.pts(), rays[index], &i); // SkASSERT(i.used() >= 1); // if (i.used() <= 1) { // continue; // } double tStart = segment.t(index ? rh.fStart : fStart); double tEnd = segment.t(index ? rh.fComputedEnd : fComputedEnd); bool testAscends = index ? rh.fStart < rh.fComputedEnd : fStart < fComputedEnd; double t = testAscends ? 0 : 1; for (int idx2 = 0; idx2 < i.used(); ++idx2) { double testT = i[0][idx2]; if (!approximately_between_orderable(tStart, testT, tEnd)) { continue; } if (approximately_equal_orderable(tStart, testT)) { continue; } smallTs[index] = t = testAscends ? SkTMax(t, testT) : SkTMin(t, testT); limited[index] = approximately_equal_orderable(t, tEnd); } } #if 0 if (smallTs[0] < 0 && smallTs[1] < 0) { // if neither ray intersects, do endpoint sort double m0xm1 = 0; if (lVerb == SkPath::kLine_Verb) { SkASSERT(rVerb != SkPath::kLine_Verb); SkDVector m0 = rays[1][1] - fCurvePart[0]; SkDPoint endPt; endPt.set(rh.fSegment->pts()[rh.fStart < rh.fEnd ? rPts : 0]); SkDVector m1 = endPt - fCurvePart[0]; m0xm1 = m0.crossCheck(m1); } if (rVerb == SkPath::kLine_Verb) { SkDPoint endPt; endPt.set(fSegment->pts()[fStart < fEnd ? lPts : 0]); SkDVector m0 = endPt - fCurvePart[0]; SkDVector m1 = rays[0][1] - fCurvePart[0]; m0xm1 = m0.crossCheck(m1); } if (m0xm1 != 0) { return m0xm1 < 0; } } #endif bool sRayLonger = false; SkDVector sCept = {0, 0}; double sCeptT = -1; int sIndex = -1; bool useIntersect = false; for (int index = 0; index < 2; ++index) { if (smallTs[index] < 0) { continue; } const SkOpSegment& segment = index ? *rh.fSegment : *fSegment; const SkDPoint& dPt = segment.dPtAtT(smallTs[index]); SkDVector cept = dPt - rays[index][0]; // If this point is on the curve, it should have been detected earlier by ordinary // curve intersection. This may be hard to determine in general, but for lines, // the point could be close to or equal to its end, but shouldn't be near the start. if ((index ? lPts : rPts) == 1) { SkDVector total = rays[index][1] - rays[index][0]; if (cept.lengthSquared() * 2 < total.lengthSquared()) { continue; } } SkDVector end = rays[index][1] - rays[index][0]; if (cept.fX * end.fX < 0 || cept.fY * end.fY < 0) { continue; } double rayDist = cept.length(); double endDist = end.length(); bool rayLonger = rayDist > endDist; if (limited[0] && limited[1] && rayLonger) { useIntersect = true; sRayLonger = rayLonger; sCept = cept; sCeptT = smallTs[index]; sIndex = index; break; } double delta = fabs(rayDist - endDist); double minX, minY, maxX, maxY; minX = minY = SK_ScalarInfinity; maxX = maxY = -SK_ScalarInfinity; const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart; int ptCount = index ? rPts : lPts; for (int idx2 = 0; idx2 <= ptCount; ++idx2) { minX = SkTMin(minX, curve[idx2].fX); minY = SkTMin(minY, curve[idx2].fY); maxX = SkTMax(maxX, curve[idx2].fX); maxY = SkTMax(maxY, curve[idx2].fY); } double maxWidth = SkTMax(maxX - minX, maxY - minY); delta /= maxWidth; if (delta > 1e-4 && (useIntersect ^= true)) { // FIXME: move this magic number sRayLonger = rayLonger; sCept = cept; sCeptT = smallTs[index]; sIndex = index; } } if (useIntersect) { const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart; const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment; double tStart = segment.t(sIndex ? rh.fStart : fStart); SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0]; double septDir = mid.crossCheck(sCept); if (!septDir) { return checkParallel(rh); } return sRayLonger ^ (sIndex == 0) ^ (septDir < 0); } else { return checkParallel(rh); } }
bool SkOpEdgeBuilder::walk() { uint8_t* verbPtr = fPathVerbs.begin(); uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; SkPoint* pointsPtr = fPathPts.begin() - 1; SkScalar* weightPtr = fWeights.begin(); SkPath::Verb verb; SkOpContour* contour = fContourBuilder.contour(); while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) { if (verbPtr == endOfFirstHalf) { fOperand = true; } verbPtr++; switch (verb) { case SkPath::kMove_Verb: if (contour && contour->count()) { if (fAllowOpenContours) { complete(); } else if (!close()) { return false; } } if (!contour) { fContourBuilder.setContour(contour = fContoursHead->appendContour()); } contour->init(fGlobalState, fOperand, fXorMask[fOperand] == kEvenOdd_PathOpsMask); pointsPtr += 1; continue; case SkPath::kLine_Verb: fContourBuilder.addLine(pointsPtr); break; case SkPath::kQuad_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; if (v1.dot(v2) < 0) { SkPoint pair[5]; if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) { goto addOneQuad; } if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { return false; } for (unsigned index = 0; index < SK_ARRAY_COUNT(pair); ++index) { force_small_to_zero(&pair[index]); } SkPoint cStorage[2][2]; SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? &pair[0] : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? &pair[2] : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fContourBuilder.addCurve(v1, curve1); fContourBuilder.addCurve(v2, curve2); break; } } } addOneQuad: fContourBuilder.addQuad(pointsPtr); break; case SkPath::kConic_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; SkScalar weight = *weightPtr++; if (v1.dot(v2) < 0) { // FIXME: max curvature for conics hasn't been implemented; use placeholder SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr); if (maxCurvature > 0) { SkConic conic(pointsPtr, weight); SkConic pair[2]; if (!conic.chopAt(maxCurvature, pair)) { // if result can't be computed, use original fContourBuilder.addConic(pointsPtr, weight); break; } SkPoint cStorage[2][3]; SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? pair[0].fPts : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? pair[1].fPts : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fContourBuilder.addCurve(v1, curve1, pair[0].fW); fContourBuilder.addCurve(v2, curve2, pair[1].fW); break; } } } fContourBuilder.addConic(pointsPtr, weight); } break; case SkPath::kCubic_Verb: { // Split complex cubics (such as self-intersecting curves or // ones with difficult curvature) in two before proceeding. // This can be required for intersection to succeed. SkScalar splitT[3]; int breaks = SkDCubic::ComplexBreak(pointsPtr, splitT); if (!breaks) { fContourBuilder.addCubic(pointsPtr); break; } SkASSERT(breaks <= (int) SK_ARRAY_COUNT(splitT)); struct Splitsville { double fT[2]; SkPoint fPts[4]; SkPoint fReduced[4]; SkPath::Verb fVerb; bool fCanAdd; } splits[4]; SkASSERT(SK_ARRAY_COUNT(splits) == SK_ARRAY_COUNT(splitT) + 1); SkTQSort(splitT, &splitT[breaks - 1]); for (int index = 0; index <= breaks; ++index) { Splitsville* split = &splits[index]; split->fT[0] = index ? splitT[index - 1] : 0; split->fT[1] = index < breaks ? splitT[index] : 1; SkDCubic part = SkDCubic::SubDivide(pointsPtr, split->fT[0], split->fT[1]); if (!part.toFloatPoints(split->fPts)) { return false; } split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced); SkPoint* curve = SkPath::kCubic_Verb == verb ? split->fPts : split->fReduced; split->fCanAdd = can_add_curve(split->fVerb, curve); } for (int index = 0; index <= breaks; ++index) { Splitsville* split = &splits[index]; if (!split->fCanAdd) { continue; } int prior = index; while (prior > 0 && !splits[prior - 1].fCanAdd) { --prior; } if (prior < index) { split->fT[0] = splits[prior].fT[0]; split->fPts[0] = splits[prior].fPts[0]; } int next = index; int breakLimit = SkTMin(breaks, (int) SK_ARRAY_COUNT(splits) - 1); while (next < breakLimit && !splits[next + 1].fCanAdd) { ++next; } if (next > index) { split->fT[1] = splits[next].fT[1]; split->fPts[3] = splits[next].fPts[3]; } if (prior < index || next > index) { split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced); } SkPoint* curve = SkPath::kCubic_Verb == split->fVerb ? split->fPts : split->fReduced; if (!can_add_curve(split->fVerb, curve)) { return false; } fContourBuilder.addCurve(split->fVerb, curve); } } break; case SkPath::kClose_Verb: SkASSERT(contour); if (!close()) { return false; } contour = nullptr; continue; default: SkDEBUGFAIL("bad verb"); return false; } SkASSERT(contour); if (contour->count()) { contour->debugValidate(); } pointsPtr += SkPathOpsVerbToPoints(verb); } fContourBuilder.flush(); if (contour && contour->count() &&!fAllowOpenContours && !close()) { return false; } return true; }